@cat-factory/app 0.146.3 → 0.147.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.
@@ -46,6 +46,10 @@ const step = computed(() => {
46
46
  const state = computed<PrReviewStepState | null>(() => step.value?.prReview ?? null)
47
47
  const status = computed(() => state.value?.status ?? null)
48
48
  const awaiting = computed(() => status.value === 'awaiting_selection')
49
+ // A finding is being re-examined by the Challenge Investigator (the whole review is `challenging`
50
+ // while it runs). The findings stay visible — the challenged one shows a spinner — but the
51
+ // selection controls + per-finding actions are disabled until the verdict lands and it re-parks.
52
+ const challenging = computed(() => status.value === 'challenging')
49
53
  // The reviewer's live todo list while it works, streamed onto the step. Its entries are the
50
54
  // cohesive slices/chunks the agent grouped the diff into (plus a final "aggregate" step), so it
51
55
  // surfaces slices-reviewed-so-far progress during the `reviewing` phase — richer than a spinner.
@@ -101,6 +105,27 @@ const groups = computed(() => {
101
105
  return out
102
106
  })
103
107
 
108
+ /** A finding the Challenge Investigator RETRACTED — it can no longer be acted on (auto-deselected). */
109
+ function isRetracted(f: PrReviewFinding): boolean {
110
+ return f.challenge?.status === 'retracted'
111
+ }
112
+ /** A finding currently being re-examined by the Challenge Investigator. */
113
+ function isInvestigating(f: PrReviewFinding): boolean {
114
+ return f.challenge?.status === 'investigating'
115
+ }
116
+ /** A finding the investigator UPHELD + strengthened after a challenge (its body actually changed). */
117
+ function isAmended(f: PrReviewFinding): boolean {
118
+ return f.challenge?.status === 'amended'
119
+ }
120
+ /** A finding the investigator UPHELD as written after a challenge (kept, no revision). */
121
+ function isUpheld(f: PrReviewFinding): boolean {
122
+ return f.challenge?.status === 'upheld'
123
+ }
124
+ /** A finding whose challenge investigation FAILED — the finding is kept as-is; re-challenge allowed. */
125
+ function isChallengeFailed(f: PrReviewFinding): boolean {
126
+ return f.challenge?.status === 'failed'
127
+ }
128
+
104
129
  // The human's selection — a set of finding ids. Defaults to every finding (the human deselects
105
130
  // the noise), so "Finish" without touching anything keeps all. Re-seeded ONLY when the set of
106
131
  // finding IDS actually changes — NOT on every execution-stream re-emit (which hands us a fresh
@@ -111,11 +136,17 @@ const selected = ref<Set<string>>(new Set())
111
136
  watch(
112
137
  findingIdKey,
113
138
  () => {
114
- selected.value = new Set(findings.value.map((f) => f.id))
139
+ selected.value = new Set(findings.value.filter((f) => !isRetracted(f)).map((f) => f.id))
115
140
  },
116
141
  { immediate: true },
117
142
  )
118
143
 
144
+ // The effective selection: checked AND not retracted. A retracted finding is never acted on even
145
+ // if its box was checked before the investigator dropped it (it's disabled + unchecked visually).
146
+ const activeSelectedIds = computed(() =>
147
+ findings.value.filter((f) => selected.value.has(f.id) && !isRetracted(f)).map((f) => f.id),
148
+ )
149
+
119
150
  function toggle(id: string): void {
120
151
  const next = new Set(selected.value)
121
152
  if (next.has(id)) next.delete(id)
@@ -123,7 +154,7 @@ function toggle(id: string): void {
123
154
  selected.value = next
124
155
  }
125
156
  function selectAll(): void {
126
- selected.value = new Set(findings.value.map((f) => f.id))
157
+ selected.value = new Set(findings.value.filter((f) => !isRetracted(f)).map((f) => f.id))
127
158
  }
128
159
  function clearAll(): void {
129
160
  selected.value = new Set()
@@ -132,13 +163,40 @@ function clearAll(): void {
132
163
  const canResolve = computed(() => awaiting.value && !prReview.resolving)
133
164
  // Fix / Post act on the selection, so they need at least one selected finding; Finish always
134
165
  // works (it just records the — possibly empty — curated selection and completes the review).
135
- const hasSelection = computed(() => selected.value.size > 0)
166
+ const hasSelection = computed(() => activeSelectedIds.value.length > 0)
136
167
 
137
168
  async function onResolve(action: PrReviewResolution): Promise<void> {
138
169
  const id = instanceId.value
139
170
  if (!id || !canResolve.value) return
140
171
  if ((action === 'fix' || action === 'post') && !hasSelection.value) return
141
- await prReview.resolve(id, [...selected.value], action).catch(() => {})
172
+ await prReview.resolve(id, activeSelectedIds.value, action).catch(() => {})
173
+ }
174
+
175
+ // Per-finding CHALLENGE: the open finding's id (its inline concern box is showing) + the drafted
176
+ // concern text. Dispatching moves the whole review to `challenging` until the verdict lands.
177
+ const challengeForId = ref<string | null>(null)
178
+ const challengeText = ref('')
179
+ function openChallenge(id: string): void {
180
+ challengeForId.value = id
181
+ challengeText.value = ''
182
+ }
183
+ function cancelChallenge(): void {
184
+ challengeForId.value = null
185
+ challengeText.value = ''
186
+ }
187
+ async function submitChallenge(id: string): Promise<void> {
188
+ const inst = instanceId.value
189
+ if (!inst || !canResolve.value) return
190
+ const question = challengeText.value.trim()
191
+ challengeForId.value = null
192
+ challengeText.value = ''
193
+ await prReview.challenge(inst, id, question || undefined).catch(() => {})
194
+ }
195
+ async function onDismiss(id: string): Promise<void> {
196
+ const inst = instanceId.value
197
+ if (!inst || !canResolve.value) return
198
+ if (challengeForId.value === id) cancelChallenge()
199
+ await prReview.dismiss(inst, id).catch(() => {})
142
200
  }
143
201
  </script>
144
202
 
@@ -335,10 +393,20 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
335
393
  </div>
336
394
 
337
395
  <template v-else>
396
+ <!-- A challenge is in flight: the Challenge Investigator is re-examining a finding. -->
397
+ <div
398
+ v-if="challenging"
399
+ data-testid="pr-review-challenging"
400
+ 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"
401
+ >
402
+ <UIcon name="i-lucide-loader-circle" class="h-4 w-4 shrink-0 animate-spin" />
403
+ <span>{{ t('prReview.challenge.investigatingBanner') }}</span>
404
+ </div>
405
+
338
406
  <!-- Selection toolbar -->
339
407
  <div v-if="awaiting" class="mb-2 flex items-center gap-3 text-[11px] text-slate-400">
340
408
  <span data-testid="pr-review-selected-count">
341
- {{ t('prReview.selectedCount', { count: selected.size }) }}
409
+ {{ t('prReview.selectedCount', { count: activeSelectedIds.length }) }}
342
410
  </span>
343
411
  <button class="text-indigo-300 hover:underline" @click="selectAll">
344
412
  {{ t('prReview.selectAll') }}
@@ -361,19 +429,21 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
361
429
  :key="f.id"
362
430
  data-testid="pr-review-finding"
363
431
  class="mb-1.5 rounded-xl border px-3 py-2 transition"
364
- :class="
365
- awaiting && selected.has(f.id)
432
+ :class="[
433
+ awaiting && selected.has(f.id) && !isRetracted(f)
366
434
  ? 'border-indigo-500/60 bg-indigo-500/5'
367
- : 'border-slate-800 bg-slate-900/60'
368
- "
435
+ : 'border-slate-800 bg-slate-900/60',
436
+ isRetracted(f) ? 'opacity-60' : '',
437
+ ]"
369
438
  >
370
439
  <div class="flex items-start gap-2">
371
440
  <input
372
- v-if="awaiting"
441
+ v-if="awaiting || challenging"
373
442
  type="checkbox"
374
443
  class="mt-1 accent-indigo-500"
375
444
  data-testid="pr-review-finding-toggle"
376
- :checked="selected.has(f.id)"
445
+ :checked="selected.has(f.id) && !isRetracted(f)"
446
+ :disabled="!awaiting || isRetracted(f)"
377
447
  @change="toggle(f.id)"
378
448
  />
379
449
  <div class="min-w-0 flex-1">
@@ -394,7 +464,47 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
394
464
  >
395
465
  {{ t('prReview.postReport.postedBadge') }}
396
466
  </span>
397
- <h4 class="min-w-0 flex-1 text-[13px] font-medium text-slate-100">
467
+ <!-- Challenge outcome badges -->
468
+ <span
469
+ v-if="isRetracted(f)"
470
+ data-testid="pr-review-finding-retracted"
471
+ 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"
472
+ >
473
+ {{ t('prReview.challenge.retractedBadge') }}
474
+ </span>
475
+ <span
476
+ v-else-if="isAmended(f)"
477
+ data-testid="pr-review-finding-amended"
478
+ 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"
479
+ >
480
+ {{ t('prReview.challenge.strengthenedBadge') }}
481
+ </span>
482
+ <span
483
+ v-else-if="isUpheld(f)"
484
+ data-testid="pr-review-finding-upheld"
485
+ 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"
486
+ >
487
+ {{ t('prReview.challenge.upheldBadge') }}
488
+ </span>
489
+ <span
490
+ v-else-if="isChallengeFailed(f)"
491
+ data-testid="pr-review-finding-challenge-failed"
492
+ 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"
493
+ >
494
+ {{ t('prReview.challenge.failedBadge') }}
495
+ </span>
496
+ <span
497
+ v-else-if="isInvestigating(f)"
498
+ data-testid="pr-review-finding-investigating"
499
+ 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"
500
+ >
501
+ <UIcon name="i-lucide-loader-circle" class="h-3 w-3 animate-spin" />
502
+ {{ t('prReview.challenge.investigatingBadge') }}
503
+ </span>
504
+ <h4
505
+ class="min-w-0 flex-1 text-[13px] font-medium text-slate-100"
506
+ :class="isRetracted(f) ? 'line-through' : ''"
507
+ >
398
508
  {{ f.title }}
399
509
  </h4>
400
510
  </div>
@@ -404,7 +514,10 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
404
514
  · {{ t('prReview.line', { line: f.line }) }}</template
405
515
  >
406
516
  </p>
407
- <p class="mt-1 whitespace-pre-wrap text-[12px] text-slate-300">
517
+ <p
518
+ class="mt-1 whitespace-pre-wrap text-[12px] text-slate-300"
519
+ :class="isRetracted(f) ? 'line-through' : ''"
520
+ >
408
521
  {{ f.detail }}
409
522
  </p>
410
523
  <p
@@ -414,6 +527,92 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
414
527
  <span class="text-slate-500">{{ t('prReview.suggestedFix') }}</span>
415
528
  {{ f.suggestedFix }}
416
529
  </p>
530
+
531
+ <!-- The investigator's justification (why the finding holds up / was retracted),
532
+ or the reason the challenge investigation failed. -->
533
+ <p
534
+ v-if="f.challenge?.justification"
535
+ data-testid="pr-review-finding-justification"
536
+ class="mt-1.5 whitespace-pre-wrap rounded-md px-2 py-1 text-[11px]"
537
+ :class="
538
+ isRetracted(f)
539
+ ? 'bg-rose-500/10 text-rose-200'
540
+ : isChallengeFailed(f)
541
+ ? 'bg-amber-500/10 text-amber-200'
542
+ : 'bg-sky-500/10 text-sky-200'
543
+ "
544
+ >
545
+ <span class="font-medium">{{
546
+ isChallengeFailed(f)
547
+ ? t('prReview.challenge.failedLabel')
548
+ : t('prReview.challenge.verdictLabel')
549
+ }}</span>
550
+ {{ f.challenge.justification }}
551
+ </p>
552
+
553
+ <!-- Per-finding actions: Challenge + Dismiss (only while awaiting a selection). -->
554
+ <div
555
+ v-if="awaiting && !isInvestigating(f)"
556
+ class="mt-1.5 flex items-center gap-3 text-[11px]"
557
+ >
558
+ <button
559
+ v-if="!isRetracted(f)"
560
+ data-testid="pr-review-finding-challenge"
561
+ class="flex items-center gap-1 text-indigo-300 hover:underline disabled:opacity-50"
562
+ :disabled="!canResolve || !access.canExecuteRuns.value"
563
+ @click="openChallenge(f.id)"
564
+ >
565
+ <UIcon name="i-lucide-gavel" class="h-3.5 w-3.5" />
566
+ {{
567
+ f.challenge
568
+ ? t('prReview.challenge.reChallenge')
569
+ : t('prReview.challenge.action')
570
+ }}
571
+ </button>
572
+ <button
573
+ data-testid="pr-review-finding-dismiss"
574
+ class="flex items-center gap-1 text-slate-400 hover:text-rose-300 hover:underline disabled:opacity-50"
575
+ :disabled="!canResolve || !access.canExecuteRuns.value"
576
+ @click="onDismiss(f.id)"
577
+ >
578
+ <UIcon name="i-lucide-trash-2" class="h-3.5 w-3.5" />
579
+ {{ t('prReview.challenge.dismiss') }}
580
+ </button>
581
+ </div>
582
+
583
+ <!-- The inline challenge box: an OPTIONAL specific concern for the investigator. -->
584
+ <div
585
+ v-if="challengeForId === f.id"
586
+ data-testid="pr-review-challenge-box"
587
+ class="mt-2 rounded-md border border-indigo-500/40 bg-slate-900/80 p-2"
588
+ >
589
+ <textarea
590
+ v-model="challengeText"
591
+ data-testid="pr-review-challenge-input"
592
+ rows="2"
593
+ :placeholder="t('prReview.challenge.placeholder')"
594
+ 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"
595
+ />
596
+ <p class="mt-1 text-[10px] text-slate-500">
597
+ {{ t('prReview.challenge.hint') }}
598
+ </p>
599
+ <div class="mt-1.5 flex justify-end gap-2">
600
+ <button
601
+ class="rounded px-2 py-1 text-[11px] text-slate-400 hover:text-slate-200"
602
+ @click="cancelChallenge"
603
+ >
604
+ {{ t('common.cancel') }}
605
+ </button>
606
+ <button
607
+ data-testid="pr-review-challenge-submit"
608
+ class="rounded bg-indigo-500/80 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-500 disabled:opacity-50"
609
+ :disabled="!canResolve"
610
+ @click="submitChallenge(f.id)"
611
+ >
612
+ {{ t('prReview.challenge.submit') }}
613
+ </button>
614
+ </div>
615
+ </div>
417
616
  </div>
418
617
  </div>
419
618
  </article>
@@ -1,4 +1,9 @@
1
- import { getPrReviewContract, resolvePrReviewContract } from '@cat-factory/contracts'
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
  }
@@ -82,5 +82,50 @@ export const usePrReviewStore = defineStore('prReview', () => {
82
82
  }
83
83
  }
84
84
 
85
- return { resolving, error, load, resolve }
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
  })
@@ -45,6 +45,7 @@ export type {
45
45
  ForkDecisionStepState,
46
46
  PrReviewStepState,
47
47
  PrReviewFinding,
48
+ PrReviewFindingChallenge,
48
49
  PrReviewSlice,
49
50
  PrReviewSeverity,
50
51
  PrReviewCategory,
@@ -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.
@@ -4948,6 +4948,22 @@
4948
4948
  "chunks": "{completed} von {total} Abschnitten geprüft",
4949
4949
  "inProgress": "{count} in Bearbeitung"
4950
4950
  },
4951
+ "challenge": {
4952
+ "action": "Anfechten",
4953
+ "reChallenge": "Erneut anfechten",
4954
+ "dismiss": "Verwerfen",
4955
+ "submit": "Untersuchen",
4956
+ "placeholder": "Optional: Was genau ist an diesem Befund falsch oder nicht überzeugend?",
4957
+ "hint": "Leer lassen, damit der Prüfer tiefer gräbt, den Befund begründet und prüft, ob er zutreffend und relevant ist.",
4958
+ "investigatingBanner": "Der Challenge-Investigator prüft einen Befund erneut anhand des Quellcodes…",
4959
+ "investigatingBadge": "Wird untersucht",
4960
+ "strengthenedBadge": "Bestärkt",
4961
+ "upheldBadge": "Bestätigt",
4962
+ "retractedBadge": "Zurückgezogen",
4963
+ "verdictLabel": "Prüfer:",
4964
+ "failedBadge": "Anfechtung fehlgeschlagen",
4965
+ "failedLabel": "Anfechtung fehlgeschlagen:"
4966
+ },
4951
4967
  "fixing": {
4952
4968
  "title": "Ausgewählte Befunde werden behoben…",
4953
4969
  "hint": "Ein Fixer committet Änderungen für die ausgewählten Befunde auf den Pull-Request-Branch."
@@ -5077,6 +5077,22 @@
5077
5077
  "chunks": "{completed} of {total} chunks reviewed",
5078
5078
  "inProgress": "{count} in progress"
5079
5079
  },
5080
+ "challenge": {
5081
+ "action": "Challenge",
5082
+ "reChallenge": "Challenge again",
5083
+ "dismiss": "Dismiss",
5084
+ "submit": "Investigate",
5085
+ "placeholder": "Optional: what specifically is wrong or unconvincing about this finding?",
5086
+ "hint": "Leave blank to have the investigator dig deeper, justify the finding, and check it's accurate and relevant.",
5087
+ "investigatingBanner": "The Challenge Investigator is re-examining a finding against the source…",
5088
+ "investigatingBadge": "Investigating",
5089
+ "strengthenedBadge": "Strengthened",
5090
+ "upheldBadge": "Upheld",
5091
+ "retractedBadge": "Retracted",
5092
+ "verdictLabel": "Investigator:",
5093
+ "failedBadge": "Challenge failed",
5094
+ "failedLabel": "Challenge failed:"
5095
+ },
5080
5096
  "fixing": {
5081
5097
  "title": "Fixing the selected findings…",
5082
5098
  "hint": "A fixer is committing changes for the selected findings onto the pull request branch."
@@ -4936,6 +4936,22 @@
4936
4936
  "chunks": "{completed} de {total} fragmentos revisados",
4937
4937
  "inProgress": "{count} en curso"
4938
4938
  },
4939
+ "challenge": {
4940
+ "action": "Cuestionar",
4941
+ "reChallenge": "Cuestionar de nuevo",
4942
+ "dismiss": "Descartar",
4943
+ "submit": "Investigar",
4944
+ "placeholder": "Opcional: ¿qué es exactamente lo incorrecto o poco convincente de este hallazgo?",
4945
+ "hint": "Déjalo en blanco para que el investigador profundice, justifique el hallazgo y compruebe que es preciso y relevante.",
4946
+ "investigatingBanner": "El Investigador de Objeciones está reexaminando un hallazgo frente al código fuente…",
4947
+ "investigatingBadge": "Investigando",
4948
+ "strengthenedBadge": "Reforzado",
4949
+ "upheldBadge": "Confirmado",
4950
+ "retractedBadge": "Retirado",
4951
+ "verdictLabel": "Investigador:",
4952
+ "failedBadge": "Cuestionamiento fallido",
4953
+ "failedLabel": "Cuestionamiento fallido:"
4954
+ },
4939
4955
  "fixing": {
4940
4956
  "title": "Corrigiendo los hallazgos seleccionados…",
4941
4957
  "hint": "Un corrector está confirmando cambios para los hallazgos seleccionados en la rama del pull request."
@@ -4936,6 +4936,22 @@
4936
4936
  "chunks": "{completed} sur {total} sections examinées",
4937
4937
  "inProgress": "{count} en cours"
4938
4938
  },
4939
+ "challenge": {
4940
+ "action": "Contester",
4941
+ "reChallenge": "Contester à nouveau",
4942
+ "dismiss": "Écarter",
4943
+ "submit": "Enquêter",
4944
+ "placeholder": "Facultatif : qu'y a-t-il précisément d'incorrect ou de peu convaincant dans ce constat ?",
4945
+ "hint": "Laissez vide pour que l'enquêteur approfondisse, justifie le constat et vérifie qu'il est exact et pertinent.",
4946
+ "investigatingBanner": "L'enquêteur de contestation réexamine un constat par rapport au code source…",
4947
+ "investigatingBadge": "Enquête en cours",
4948
+ "strengthenedBadge": "Renforcé",
4949
+ "upheldBadge": "Confirmé",
4950
+ "retractedBadge": "Retiré",
4951
+ "verdictLabel": "Enquêteur :",
4952
+ "failedBadge": "Échec de la contestation",
4953
+ "failedLabel": "Échec de la contestation :"
4954
+ },
4939
4955
  "fixing": {
4940
4956
  "title": "Correction des points sélectionnés…",
4941
4957
  "hint": "Un correcteur valide des modifications pour les points sélectionnés sur la branche de la pull request."
@@ -4947,6 +4947,22 @@
4947
4947
  "chunks": "{completed} מתוך {total} מקטעים נבדקו",
4948
4948
  "inProgress": "{count} בתהליך"
4949
4949
  },
4950
+ "challenge": {
4951
+ "action": "ערער",
4952
+ "reChallenge": "ערער שוב",
4953
+ "dismiss": "התעלם",
4954
+ "submit": "חקור",
4955
+ "placeholder": "רשות: מה בדיוק שגוי או לא משכנע בממצא הזה?",
4956
+ "hint": "השאירו ריק כדי שהחוקר יעמיק, ינמק את הממצא ויוודא שהוא מדויק ורלוונטי.",
4957
+ "investigatingBanner": "חוקר הערעורים בוחן מחדש ממצא מול קוד המקור…",
4958
+ "investigatingBadge": "בחקירה",
4959
+ "strengthenedBadge": "חוזק",
4960
+ "upheldBadge": "אושר",
4961
+ "retractedBadge": "בוטל",
4962
+ "verdictLabel": "חוקר:",
4963
+ "failedBadge": "הערעור נכשל",
4964
+ "failedLabel": "הערעור נכשל:"
4965
+ },
4950
4966
  "fixing": {
4951
4967
  "title": "מתקן את הממצאים שנבחרו…",
4952
4968
  "hint": "מתקן מבצע commit לשינויים עבור הממצאים שנבחרו אל ענף בקשת המשיכה."
@@ -4948,6 +4948,22 @@
4948
4948
  "chunks": "{completed} di {total} sezioni esaminate",
4949
4949
  "inProgress": "{count} in corso"
4950
4950
  },
4951
+ "challenge": {
4952
+ "action": "Contesta",
4953
+ "reChallenge": "Contesta di nuovo",
4954
+ "dismiss": "Scarta",
4955
+ "submit": "Indaga",
4956
+ "placeholder": "Facoltativo: cosa c'è di preciso di sbagliato o poco convincente in questo rilievo?",
4957
+ "hint": "Lascia vuoto per far approfondire l'investigatore, giustificare il rilievo e verificarne accuratezza e pertinenza.",
4958
+ "investigatingBanner": "L'investigatore delle contestazioni sta riesaminando un rilievo rispetto al codice sorgente…",
4959
+ "investigatingBadge": "In esame",
4960
+ "strengthenedBadge": "Rafforzato",
4961
+ "upheldBadge": "Confermato",
4962
+ "retractedBadge": "Ritirato",
4963
+ "verdictLabel": "Investigatore:",
4964
+ "failedBadge": "Contestazione non riuscita",
4965
+ "failedLabel": "Contestazione non riuscita:"
4966
+ },
4951
4967
  "fixing": {
4952
4968
  "title": "Correzione dei rilievi selezionati…",
4953
4969
  "hint": "Un fixer sta effettuando il commit delle modifiche per i rilievi selezionati sul branch della pull request."
@@ -4948,6 +4948,22 @@
4948
4948
  "chunks": "{total} 個中 {completed} 個のチャンクをレビュー済み",
4949
4949
  "inProgress": "{count} 件処理中"
4950
4950
  },
4951
+ "challenge": {
4952
+ "action": "異議を唱える",
4953
+ "reChallenge": "再度異議を唱える",
4954
+ "dismiss": "却下",
4955
+ "submit": "調査する",
4956
+ "placeholder": "任意: この指摘のどこが誤り、または納得できないかを記入してください。",
4957
+ "hint": "空欄のままにすると、調査担当がさらに深く掘り下げ、指摘の根拠を示し、正確性と関連性を検証します。",
4958
+ "investigatingBanner": "異議調査担当がソースコードに照らして指摘を再検証しています…",
4959
+ "investigatingBadge": "調査中",
4960
+ "strengthenedBadge": "強化済み",
4961
+ "upheldBadge": "支持",
4962
+ "retractedBadge": "撤回済み",
4963
+ "verdictLabel": "調査担当:",
4964
+ "failedBadge": "異議調査に失敗",
4965
+ "failedLabel": "異議調査に失敗:"
4966
+ },
4951
4967
  "fixing": {
4952
4968
  "title": "選択した指摘を修正中…",
4953
4969
  "hint": "フィクサーが選択した指摘の変更をプルリクエストのブランチにコミットしています。"
@@ -4936,6 +4936,22 @@
4936
4936
  "chunks": "Sprawdzono {completed} z {total} fragmentów",
4937
4937
  "inProgress": "{count} w toku"
4938
4938
  },
4939
+ "challenge": {
4940
+ "action": "Zakwestionuj",
4941
+ "reChallenge": "Zakwestionuj ponownie",
4942
+ "dismiss": "Odrzuć",
4943
+ "submit": "Zbadaj",
4944
+ "placeholder": "Opcjonalnie: co dokładnie jest błędne lub nieprzekonujące w tym ustaleniu?",
4945
+ "hint": "Pozostaw puste, aby badacz zgłębił temat, uzasadnił ustalenie i sprawdził, czy jest trafne i istotne.",
4946
+ "investigatingBanner": "Badacz zastrzeżeń ponownie analizuje ustalenie w oparciu o kod źródłowy…",
4947
+ "investigatingBadge": "Badanie",
4948
+ "strengthenedBadge": "Wzmocnione",
4949
+ "upheldBadge": "Podtrzymane",
4950
+ "retractedBadge": "Wycofane",
4951
+ "verdictLabel": "Badacz:",
4952
+ "failedBadge": "Zakwestionowanie nie powiodło się",
4953
+ "failedLabel": "Zakwestionowanie nie powiodło się:"
4954
+ },
4939
4955
  "fixing": {
4940
4956
  "title": "Naprawianie wybranych uwag…",
4941
4957
  "hint": "Fixer zatwierdza zmiany dla wybranych uwag w gałęzi pull requesta."
@@ -4948,6 +4948,22 @@
4948
4948
  "chunks": "{total} bölümden {completed} tanesi incelendi",
4949
4949
  "inProgress": "{count} devam ediyor"
4950
4950
  },
4951
+ "challenge": {
4952
+ "action": "İtiraz et",
4953
+ "reChallenge": "Tekrar itiraz et",
4954
+ "dismiss": "Yoksay",
4955
+ "submit": "İncele",
4956
+ "placeholder": "İsteğe bağlı: Bu bulguda tam olarak yanlış veya ikna edici olmayan nedir?",
4957
+ "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.",
4958
+ "investigatingBanner": "İtiraz Araştırmacısı bir bulguyu kaynak koda göre yeniden inceliyor…",
4959
+ "investigatingBadge": "İnceleniyor",
4960
+ "strengthenedBadge": "Güçlendirildi",
4961
+ "upheldBadge": "Onaylandı",
4962
+ "retractedBadge": "Geri çekildi",
4963
+ "verdictLabel": "Araştırmacı:",
4964
+ "failedBadge": "İtiraz başarısız oldu",
4965
+ "failedLabel": "İtiraz başarısız oldu:"
4966
+ },
4951
4967
  "fixing": {
4952
4968
  "title": "Seçili bulgular düzeltiliyor…",
4953
4969
  "hint": "Bir düzeltici, seçili bulgular için değişiklikleri pull request dalına işliyor."
@@ -4936,6 +4936,22 @@
4936
4936
  "chunks": "Перевірено {completed} з {total} фрагментів",
4937
4937
  "inProgress": "{count} у процесі"
4938
4938
  },
4939
+ "challenge": {
4940
+ "action": "Оскаржити",
4941
+ "reChallenge": "Оскаржити знову",
4942
+ "dismiss": "Відхилити",
4943
+ "submit": "Дослідити",
4944
+ "placeholder": "Необов'язково: що саме неправильне або непереконливе в цьому зауваженні?",
4945
+ "hint": "Залиште порожнім, щоб дослідник копнув глибше, обґрунтував зауваження та перевірив його точність і доречність.",
4946
+ "investigatingBanner": "Дослідник оскаржень повторно перевіряє зауваження за вихідним кодом…",
4947
+ "investigatingBadge": "Дослідження",
4948
+ "strengthenedBadge": "Посилено",
4949
+ "upheldBadge": "Підтверджено",
4950
+ "retractedBadge": "Відкликано",
4951
+ "verdictLabel": "Дослідник:",
4952
+ "failedBadge": "Оскарження не вдалося",
4953
+ "failedLabel": "Оскарження не вдалося:"
4954
+ },
4939
4955
  "fixing": {
4940
4956
  "title": "Виправлення вибраних зауважень…",
4941
4957
  "hint": "Фіксер комітить зміни для вибраних зауважень у гілку pull request."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.146.3",
3
+ "version": "0.147.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",
@@ -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.153.0"
43
+ "@cat-factory/contracts": "0.154.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",