@cat-factory/app 0.72.0 → 0.73.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.
Files changed (43) hide show
  1. package/app/components/board/nodes/TaskCard.vue +12 -2
  2. package/app/components/bootstrap/BootstrapModal.vue +23 -7
  3. package/app/components/brainstorm/BrainstormWindow.vue +2 -0
  4. package/app/components/clarity/ClarityReviewWindow.vue +2 -0
  5. package/app/components/consensus/ConsensusSessionWindow.vue +2 -0
  6. package/app/components/focus/BlockFocusView.vue +2 -0
  7. package/app/components/followUp/FollowUpWindow.vue +2 -0
  8. package/app/components/gates/GateResultView.vue +2 -0
  9. package/app/components/humanTest/HumanTestWindow.vue +2 -0
  10. package/app/components/layout/ConnectionStatusBanner.vue +81 -0
  11. package/app/components/layout/NotificationsInbox.vue +15 -0
  12. package/app/components/panels/GenericStructuredResultView.vue +2 -0
  13. package/app/components/panels/InspectorPanel.vue +30 -1
  14. package/app/components/panels/inspector/TaskExecution.vue +25 -1
  15. package/app/components/pipeline/PipelineBuilder.vue +110 -7
  16. package/app/components/requirements/RequirementsReviewWindow.vue +2 -0
  17. package/app/components/spec/ServiceSpecWindow.vue +2 -0
  18. package/app/components/testing/TestReportWindow.vue +91 -0
  19. package/app/composables/useKeyboardShortcuts.ts +10 -2
  20. package/app/pages/index.vue +6 -4
  21. package/app/stores/board.spec.ts +58 -1
  22. package/app/stores/board.ts +59 -15
  23. package/app/stores/brainstorm.spec.ts +35 -0
  24. package/app/stores/brainstorm.ts +11 -2
  25. package/app/stores/clarity.spec.ts +33 -0
  26. package/app/stores/clarity.ts +11 -2
  27. package/app/stores/execution.spec.ts +71 -13
  28. package/app/stores/execution.ts +44 -6
  29. package/app/stores/pipelines.ts +53 -0
  30. package/app/stores/recurringPipelines.ts +5 -1
  31. package/app/stores/requirements.spec.ts +21 -0
  32. package/app/stores/requirements.ts +11 -2
  33. package/app/stores/workspace.ts +1 -1
  34. package/app/utils/catalog.ts +9 -0
  35. package/i18n/locales/en.json +56 -5
  36. package/i18n/locales/es.json +56 -5
  37. package/i18n/locales/fr.json +56 -5
  38. package/i18n/locales/he.json +56 -5
  39. package/i18n/locales/ja.json +56 -5
  40. package/i18n/locales/pl.json +56 -5
  41. package/i18n/locales/tr.json +56 -5
  42. package/i18n/locales/uk.json +56 -5
  43. package/package.json +2 -2
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
2
2
  import { computed, ref } from 'vue'
3
3
  import type { AgentKind, Pipeline } from '~/types/domain'
4
4
  import type { ConsensusStepConfig, StepGating } from '~/types/consensus'
5
+ import type { TesterQualityConfig } from '@cat-factory/contracts'
5
6
  import { companionForProducer, uid } from '~/utils/catalog'
6
7
  import { useWorkspaceStore } from '~/stores/workspace'
7
8
 
@@ -59,6 +60,13 @@ export const usePipelinesStore = defineStore('pipelines', () => {
59
60
  * a `coder` step; `false` disables the companion there (default/true ⇒ enabled).
60
61
  */
61
62
  const draftFollowUps = ref<(boolean | null)[]>([])
63
+ /**
64
+ * Per-step test quality-control companion config, kept index-aligned with `draft`. Only
65
+ * meaningful on a Tester step (`tester-api`/`tester-ui`); `null`/absent means "enabled, no
66
+ * gating" (the QC companion is on by default), `{ enabled: false }` disables it, and an
67
+ * entry with `gating` makes it conditional on the task estimate.
68
+ */
69
+ const draftTesterQuality = ref<(TesterQualityConfig | null)[]>([])
62
70
  /** Organizational labels for the pipeline being assembled/edited. */
63
71
  const draftLabels = ref<string[]>([])
64
72
  const draftName = ref('New pipeline')
@@ -84,6 +92,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
84
92
  draftConsensus.value.splice(index, 0, null)
85
93
  draftGating.value.splice(index, 0, null)
86
94
  draftFollowUps.value.splice(index, 0, null)
95
+ draftTesterQuality.value.splice(index, 0, null)
87
96
  }
88
97
 
89
98
  function addToDraft(kind: AgentKind) {
@@ -98,6 +107,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
98
107
  draftConsensus.value.splice(index, 1)
99
108
  draftGating.value.splice(index, 1)
100
109
  draftFollowUps.value.splice(index, 1)
110
+ draftTesterQuality.value.splice(index, 1)
101
111
  }
102
112
 
103
113
  function moveInDraft(from: number, to: number) {
@@ -116,6 +126,8 @@ export const usePipelinesStore = defineStore('pipelines', () => {
116
126
  draftGating.value.splice(to, 0, gat ?? null)
117
127
  const [fu] = draftFollowUps.value.splice(from, 1)
118
128
  draftFollowUps.value.splice(to, 0, fu ?? null)
129
+ const [tq] = draftTesterQuality.value.splice(from, 1)
130
+ draftTesterQuality.value.splice(to, 0, tq ?? null)
119
131
  }
120
132
 
121
133
  /** Whether the producer step at `index` currently has its companion attached after it. */
@@ -191,6 +203,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
191
203
  draftConsensus.value = reorder(draftConsensus.value)
192
204
  draftGating.value = reorder(draftGating.value)
193
205
  draftFollowUps.value = reorder(draftFollowUps.value)
206
+ draftTesterQuality.value = reorder(draftTesterQuality.value)
194
207
  }
195
208
 
196
209
  /** Toggle the consensus mechanism on the draft step at `index` (default config / off). */
@@ -214,6 +227,33 @@ export const usePipelinesStore = defineStore('pipelines', () => {
214
227
  draftFollowUps.value[index] = draftFollowUps.value[index] === false ? null : false
215
228
  }
216
229
 
230
+ /**
231
+ * Toggle the test quality-control companion on the draft (Tester) step at `index`. The
232
+ * companion is enabled by default (a `null` entry), so the first toggle disables it
233
+ * (`{ enabled: false }`, dropping any gating) and the next restores the default.
234
+ */
235
+ function toggleDraftTesterQuality(index: number) {
236
+ draftTesterQuality.value[index] =
237
+ draftTesterQuality.value[index]?.enabled === false ? null : { enabled: false }
238
+ }
239
+
240
+ /**
241
+ * Toggle estimate gating on/off for the QC companion on the draft (Tester) step at `index`.
242
+ * A no-op while the companion is disabled (nothing to gate). Enabling gating pins the config
243
+ * to `{ enabled: true, gating }` so the thresholds are editable; disabling drops back to the
244
+ * default `null` (enabled, ungated).
245
+ */
246
+ function toggleDraftTesterQualityGating(index: number) {
247
+ const cur = draftTesterQuality.value[index]
248
+ if (cur?.enabled === false) return
249
+ draftTesterQuality.value[index] = cur?.gating?.enabled
250
+ ? null
251
+ : {
252
+ enabled: true,
253
+ gating: { enabled: true, minRisk: 0.5, minImpact: 0.5, onMissingEstimate: 'run' },
254
+ }
255
+ }
256
+
217
257
  /** Enable/disable the draft step at `index` without removing it. */
218
258
  function toggleDraftEnabled(index: number) {
219
259
  draftEnabled.value[index] = draftEnabled.value[index] === false
@@ -227,6 +267,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
227
267
  draftConsensus.value = []
228
268
  draftGating.value = []
229
269
  draftFollowUps.value = []
270
+ draftTesterQuality.value = []
230
271
  draftLabels.value = []
231
272
  draftName.value = 'New pipeline'
232
273
  editingId.value = null
@@ -241,6 +282,9 @@ export const usePipelinesStore = defineStore('pipelines', () => {
241
282
  draftConsensus.value = pipeline.agentKinds.map((_, i) => pipeline.consensus?.[i] ?? null)
242
283
  draftGating.value = pipeline.agentKinds.map((_, i) => pipeline.gating?.[i] ?? null)
243
284
  draftFollowUps.value = pipeline.agentKinds.map((_, i) => pipeline.followUps?.[i] ?? null)
285
+ draftTesterQuality.value = pipeline.agentKinds.map(
286
+ (_, i) => pipeline.testerQuality?.[i] ?? null,
287
+ )
244
288
  draftLabels.value = [...(pipeline.labels ?? [])]
245
289
  draftName.value = pipeline.name
246
290
  editingId.value = pipeline.id
@@ -270,6 +314,12 @@ export const usePipelinesStore = defineStore('pipelines', () => {
270
314
  ...(draftFollowUps.value.some((f) => f === false)
271
315
  ? { followUps: [...draftFollowUps.value] }
272
316
  : {}),
317
+ // Only send testerQuality when at least one Tester step deviates from the default
318
+ // (companion disabled, or an estimate gate configured) — the default (null/enabled,
319
+ // ungated) is not worth persisting.
320
+ ...(draftTesterQuality.value.some((q) => q?.enabled === false || q?.gating?.enabled)
321
+ ? { testerQuality: [...draftTesterQuality.value] }
322
+ : {}),
273
323
  // Only send labels when there are any.
274
324
  ...(draftLabels.value.length ? { labels: [...draftLabels.value] } : {}),
275
325
  }
@@ -342,6 +392,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
342
392
  draftConsensus,
343
393
  draftGating,
344
394
  draftFollowUps,
395
+ draftTesterQuality,
345
396
  draftLabels,
346
397
  draftName,
347
398
  editingId,
@@ -357,6 +408,8 @@ export const usePipelinesStore = defineStore('pipelines', () => {
357
408
  toggleDraftGating,
358
409
  toggleDraftGate,
359
410
  toggleDraftFollowUps,
411
+ toggleDraftTesterQuality,
412
+ toggleDraftTesterQualityGating,
360
413
  toggleDraftEnabled,
361
414
  toggleDraftConsensus,
362
415
  setDraftConsensus,
@@ -13,6 +13,10 @@ import { useBoardStore } from '~/stores/board'
13
13
  export const useRecurringPipelinesStore = defineStore('recurringPipelines', () => {
14
14
  const api = useApi()
15
15
  const toast = useToast()
16
+ // Resolve translations through the Nuxt app's global i18n instance — a store runs outside a
17
+ // component `setup`, so `useI18n()` is unavailable (see the board store for the same pattern).
18
+ const nuxtApp = useNuxtApp()
19
+ const tr = (key: string): string => (nuxtApp.$i18n as { t: (k: string) => string }).t(key)
16
20
 
17
21
  const schedules = ref<PipelineSchedule[]>([])
18
22
  /** Lazily-loaded run history, keyed by schedule id. */
@@ -68,7 +72,7 @@ export const useRecurringPipelinesStore = defineStore('recurringPipelines', () =
68
72
  schedules.value = prevSchedules
69
73
  if (blockSnap) board.reattach(blockSnap)
70
74
  toast.add({
71
- title: 'Could not delete recurring pipeline',
75
+ title: tr('board.toast.recurringDeleteFailed'),
72
76
  description: e instanceof Error ? e.message : String(e),
73
77
  icon: 'i-lucide-triangle-alert',
74
78
  color: 'error',
@@ -92,3 +92,24 @@ describe('requirements store load() loading flag', () => {
92
92
  expect(calls).toBe(2)
93
93
  })
94
94
  })
95
+
96
+ describe('requirements store live-event upsert guard', () => {
97
+ it('an out-of-order stream event cannot revert a newer cached review', () => {
98
+ const store = useRequirementsStore()
99
+ // The API response for a just-submitted answer landed first (newer updatedAt)…
100
+ store.upsert(review({ updatedAt: 2000, status: 'merged' }))
101
+ // …then the slightly-older stream event (emitted just before) arrives late.
102
+ store.upsert(review({ updatedAt: 1000, status: 'ready' }))
103
+ expect(store.reviewFor('b1')?.status).toBe('merged')
104
+ // A genuinely newer event still applies.
105
+ store.upsert(review({ updatedAt: 3000, status: 'incorporated' }))
106
+ expect(store.reviewFor('b1')?.status).toBe('incorporated')
107
+ })
108
+
109
+ it('a NEW review (different id) for the block replaces regardless of updatedAt', () => {
110
+ const store = useRequirementsStore()
111
+ store.upsert(review({ updatedAt: 2000 }))
112
+ store.upsert(review({ id: 'rr2', updatedAt: 1000 }))
113
+ expect(store.reviewFor('b1')?.id).toBe('rr2')
114
+ })
115
+ })
@@ -97,6 +97,16 @@ export const useRequirementsStore = defineStore('requirements', () => {
97
97
  reviews.value = { ...reviews.value, [review.blockId]: review }
98
98
  }
99
99
 
100
+ /** Patch the cache from a live `requirements` stream event (newest wins per block). */
101
+ function upsert(review: RequirementReview) {
102
+ const existing = reviews.value[review.blockId]
103
+ // Keep the freshest by updatedAt (the consensus-store guard): `store()` also runs on
104
+ // API responses, so a slightly-older event racing a just-submitted answer over the
105
+ // separate WS transport must not revert the review the response already delivered.
106
+ if (existing && existing.id === review.id && existing.updatedAt > review.updatedAt) return
107
+ store(review)
108
+ }
109
+
100
110
  /** Drop all cached reviews + in-flight state (called on workspace switch). */
101
111
  function reset() {
102
112
  available.value = null
@@ -277,7 +287,6 @@ export const useRequirementsStore = defineStore('requirements', () => {
277
287
  rejectRecommendation,
278
288
  reRequestRecommendation,
279
289
  reset,
280
- // Patch the cache from a live `requirements` stream event.
281
- upsert: store,
290
+ upsert,
282
291
  }
283
292
  })
@@ -91,7 +91,7 @@ export const useWorkspaceStore = defineStore(
91
91
  else workspaces.value.unshift(snapshot.workspace)
92
92
  useBoardStore().hydrate(snapshot.blocks)
93
93
  usePipelinesStore().hydrate(snapshot.pipelines, snapshot.pipelineCatalogVersions)
94
- useExecutionStore().hydrate(snapshot.executions)
94
+ useExecutionStore().hydrate(snapshot.executions, snapshot.workspace.id)
95
95
  useAgentRunsStore().hydrate(snapshot.bootstrapJobs ?? [], snapshot.workspace.id)
96
96
  useAgentRunsStore().hydrateEnvConfigRepair(snapshot.envConfigRepairJobs ?? [])
97
97
  useNotificationsStore().hydrate(snapshot.notifications ?? [])
@@ -312,6 +312,15 @@ export function isConsensusEligibleKind(kind: string): boolean {
312
312
  return CONSENSUS_ELIGIBLE_KINDS.has(kind)
313
313
  }
314
314
 
315
+ /**
316
+ * Whether an agent kind is one of the Tester gate kinds (API or UI). Mirrors the backend
317
+ * `isTesterKind`; used by the pipeline builder to surface the test quality-control companion
318
+ * toggle only on Tester steps.
319
+ */
320
+ export function isTesterKind(kind: string): boolean {
321
+ return kind === 'tester-api' || kind === 'tester-ui'
322
+ }
323
+
315
324
  /**
316
325
  * Display metadata for the engine-driven "system" kinds — the gate/automation
317
326
  * steps (blueprint mapper, conflicts gate + resolver, CI gate + fixer, merger)
@@ -1,4 +1,10 @@
1
1
  {
2
+ "app": {
3
+ "loading": "Loading…",
4
+ "loadingBoard": "Loading board…",
5
+ "backendUnreachable": "Can't reach the backend",
6
+ "reconnecting": "Reconnecting…"
7
+ },
2
8
  "language": {
3
9
  "switcher": "Language",
4
10
  "warning": {
@@ -83,6 +89,14 @@
83
89
  "accountSettings": "Account settings"
84
90
  },
85
91
  "board": {
92
+ "toast": {
93
+ "updateFailed": "Could not save changes",
94
+ "epicFailed": "Could not change epic",
95
+ "moveFailed": "Could not move",
96
+ "deleteFailed": "Could not delete",
97
+ "linkFailed": "Could not link tasks",
98
+ "recurringDeleteFailed": "Could not delete recurring pipeline"
99
+ },
86
100
  "repoTypes": {
87
101
  "service": "Service",
88
102
  "frontend": "Frontend",
@@ -291,6 +305,11 @@
291
305
  "openPrOnGithub": "Open {pr} on GitHub",
292
306
  "review": "Review",
293
307
  "merge": "Merge",
308
+ "mergeConfirm": {
309
+ "title": "Merge this pull request?",
310
+ "body": "This merges the PR into its base branch and completes the task. This can't be undone.",
311
+ "confirm": "Merge"
312
+ },
294
313
  "implemented": "implemented",
295
314
  "module": "Module: {name}",
296
315
  "buildSteps": "Build steps",
@@ -645,6 +664,16 @@
645
664
  "stopTooltip": "Stop the run but keep it (readable and retryable)",
646
665
  "reset": "Reset",
647
666
  "resetTooltip": "Discard this run and reset the task to planned",
667
+ "resetConfirm": {
668
+ "title": "Discard this run?",
669
+ "body": "This deletes the run and returns the task to planned. This can't be undone.",
670
+ "confirm": "Discard run"
671
+ },
672
+ "mergeConfirm": {
673
+ "title": "Merge this pull request?",
674
+ "body": "This merges the PR into its base branch and completes the task. This can't be undone.",
675
+ "confirm": "Merge"
676
+ },
648
677
  "viewDetailsOutput": "View details and read output",
649
678
  "viewDetails": "View step details",
650
679
  "companion": "Companion",
@@ -1228,7 +1257,9 @@
1228
1257
  "dismiss": "Dismiss",
1229
1258
  "toast": {
1230
1259
  "acted": "Marked as handled",
1231
- "dismissed": "Dismissed"
1260
+ "dismissed": "Dismissed",
1261
+ "actFailed": "Could not complete that action",
1262
+ "dismissFailed": "Could not dismiss"
1232
1263
  },
1233
1264
  "action": {
1234
1265
  "merge_review": "Merge",
@@ -2675,6 +2706,10 @@
2675
2706
  "consensusRevertTooltip": "Consensus enabled. Click to revert to a single agent.",
2676
2707
  "followUpEnableTooltip": "Follow-up companion disabled. Click to enable (Coder surfaces loose ends / questions).",
2677
2708
  "followUpDisableTooltip": "Follow-up companion enabled. Coder surfaces loose ends / side-tasks / questions; click to disable.",
2709
+ "testerQualityEnableTooltip": "Test quality companion disabled. Click to enable (audits the report for coverage and loops the Tester on gaps).",
2710
+ "testerQualityDisableTooltip": "Test quality companion enabled. Audits the report for coverage before greenlight and loops the Tester on gaps; click to disable.",
2711
+ "testerQualityLabel": "Test quality companion",
2712
+ "testerQualityGateTooltip": "Only run the quality audit when the task estimate clears a threshold (needs a Task Estimator earlier)",
2678
2713
  "moveUp": "Move step up",
2679
2714
  "moveDown": "Move step down",
2680
2715
  "removeStep": "Remove this step from the pipeline",
@@ -3365,6 +3400,15 @@
3365
3400
  "failed": "Failed",
3366
3401
  "addressed": "Addressing"
3367
3402
  },
3403
+ "quality": {
3404
+ "heading": "Coverage review",
3405
+ "reruns": "Quality-driven Tester re-runs",
3406
+ "rerunCount": "{attempts}/{max} re-runs",
3407
+ "exceeded": "Budget spent",
3408
+ "adequate": "Coverage adequate",
3409
+ "inadequate": "Coverage gaps found",
3410
+ "gaps": "Gaps to close"
3411
+ },
3368
3412
  "empty": {
3369
3413
  "title": "No test report yet.",
3370
3414
  "hint": "The report appears once the Tester finishes a pass. While it runs, the step shows live progress on the board."
@@ -3837,6 +3881,7 @@
3837
3881
  },
3838
3882
  "targetRepo": {
3839
3883
  "label": "Target repository name",
3884
+ "namePlaceholder": "payments-service",
3840
3885
  "descWithOwner": "Create a fresh repo with this name under {owner}, then bootstrap pushes into it. A prepopulated README, .gitignore or license is fine.",
3841
3886
  "descNoOwner": "Create a fresh repo with this name, then bootstrap pushes into it. A prepopulated README, .gitignore or license is fine."
3842
3887
  },
@@ -3856,7 +3901,8 @@
3856
3901
  },
3857
3902
  "description": {
3858
3903
  "label": "Description",
3859
- "help": "Optional one-line summary for the repo."
3904
+ "help": "Optional one-line summary for the repo.",
3905
+ "placeholder": "Handles payment intents and refunds"
3860
3906
  },
3861
3907
  "instructions": {
3862
3908
  "labelReference": "Extra instructions for the bootstrapper",
@@ -3886,18 +3932,23 @@
3886
3932
  "add": "Add",
3887
3933
  "pickRepo": {
3888
3934
  "label": "Pick an existing GitHub repo",
3889
- "description": "Choose a repo you can access to fill in its owner and name, or enter them manually below."
3935
+ "description": "Choose a repo you can access to fill in its owner and name, or enter them manually below.",
3936
+ "placeholder": "owner/name"
3890
3937
  },
3891
3938
  "name": {
3892
3939
  "label": "Name",
3893
- "description": "A friendly label for this base."
3940
+ "description": "A friendly label for this base.",
3941
+ "placeholder": "Service Template"
3894
3942
  },
3895
3943
  "repoOwner": "Repo owner",
3944
+ "repoOwnerPlaceholder": "acme",
3896
3945
  "repoName": "Repo name",
3946
+ "repoNamePlaceholder": "service-template",
3897
3947
  "descriptionPlaceholder": "Optional summary of this base",
3898
3948
  "defaultInstructions": {
3899
3949
  "label": "Default bootstrapper instructions",
3900
- "description": "Prepended to the per-run instructions whenever this base is used."
3950
+ "description": "Prepended to the per-run instructions whenever this base is used.",
3951
+ "placeholder": "e.g. keep the structure; rename packages to match the new service"
3901
3952
  }
3902
3953
  },
3903
3954
  "toast": {
@@ -1,4 +1,10 @@
1
1
  {
2
+ "app": {
3
+ "loading": "Cargando…",
4
+ "loadingBoard": "Cargando tablero…",
5
+ "backendUnreachable": "No se puede conectar con el backend",
6
+ "reconnecting": "Reconectando…"
7
+ },
2
8
  "language": {
3
9
  "switcher": "Idioma",
4
10
  "warning": {
@@ -68,6 +74,14 @@
68
74
  "infrastructure": "Infraestructura"
69
75
  },
70
76
  "board": {
77
+ "toast": {
78
+ "updateFailed": "No se pudieron guardar los cambios",
79
+ "epicFailed": "No se pudo cambiar la épica",
80
+ "moveFailed": "No se pudo mover",
81
+ "deleteFailed": "No se pudo eliminar",
82
+ "linkFailed": "No se pudieron vincular las tareas",
83
+ "recurringDeleteFailed": "No se pudo eliminar el pipeline recurrente"
84
+ },
71
85
  "repoTypes": {
72
86
  "service": "Servicio",
73
87
  "frontend": "Frontend",
@@ -260,6 +274,11 @@
260
274
  "openPrOnGithub": "Abrir {pr} en GitHub",
261
275
  "review": "Revisar",
262
276
  "merge": "Fusionar",
277
+ "mergeConfirm": {
278
+ "title": "¿Fusionar esta pull request?",
279
+ "body": "Esto fusiona la PR en su rama base y completa la tarea. Esto no se puede deshacer.",
280
+ "confirm": "Fusionar"
281
+ },
263
282
  "implemented": "implementada",
264
283
  "module": "Módulo: {name}",
265
284
  "buildSteps": "Pasos de build",
@@ -601,6 +620,16 @@
601
620
  "stopTooltip": "Detener la ejecución pero conservarla (legible y reintentable)",
602
621
  "reset": "Restablecer",
603
622
  "resetTooltip": "Descartar esta ejecución y restablecer la tarea a planificada",
623
+ "resetConfirm": {
624
+ "title": "¿Descartar esta ejecución?",
625
+ "body": "Esto elimina la ejecución y devuelve la tarea a planificada. Esto no se puede deshacer.",
626
+ "confirm": "Descartar ejecución"
627
+ },
628
+ "mergeConfirm": {
629
+ "title": "¿Fusionar esta pull request?",
630
+ "body": "Esto fusiona la PR en su rama base y completa la tarea. Esto no se puede deshacer.",
631
+ "confirm": "Fusionar"
632
+ },
604
633
  "viewDetailsOutput": "Ver detalles y leer la salida",
605
634
  "viewDetails": "Ver detalles del paso",
606
635
  "companion": "Acompañante",
@@ -1200,7 +1229,9 @@
1200
1229
  },
1201
1230
  "toast": {
1202
1231
  "acted": "Marcado como resuelto",
1203
- "dismissed": "Descartado"
1232
+ "dismissed": "Descartado",
1233
+ "actFailed": "No se pudo completar esa acción",
1234
+ "dismissFailed": "No se pudo descartar"
1204
1235
  }
1205
1236
  },
1206
1237
  "aiProvidersBanner": {
@@ -2601,6 +2632,10 @@
2601
2632
  "consensusRevertTooltip": "Consenso activado. Haz clic para volver a un solo agente.",
2602
2633
  "followUpEnableTooltip": "Compañero de seguimiento desactivado. Haz clic para activarlo (Coder detecta cabos sueltos / preguntas).",
2603
2634
  "followUpDisableTooltip": "Compañero de seguimiento activado. Coder detecta cabos sueltos / subtareas / preguntas; haz clic para desactivarlo.",
2635
+ "testerQualityEnableTooltip": "Compañero de calidad de pruebas desactivado. Haz clic para activarlo (audita la cobertura del informe y reitera el Tester ante lagunas).",
2636
+ "testerQualityDisableTooltip": "Compañero de calidad de pruebas activado. Audita la cobertura del informe antes de la aprobación y reitera el Tester ante lagunas; haz clic para desactivarlo.",
2637
+ "testerQualityLabel": "Compañero de calidad de pruebas",
2638
+ "testerQualityGateTooltip": "Ejecutar la auditoría de calidad solo cuando la estimación de la tarea supere un umbral (requiere un Estimador de Tareas antes)",
2604
2639
  "moveUp": "Subir el paso",
2605
2640
  "moveDown": "Bajar el paso",
2606
2641
  "removeStep": "Quitar este paso del pipeline",
@@ -3242,6 +3277,15 @@
3242
3277
  "failed": "Falló",
3243
3278
  "addressed": "Abordando"
3244
3279
  },
3280
+ "quality": {
3281
+ "heading": "Revisión de cobertura",
3282
+ "reruns": "Reejecuciones del Tester impulsadas por calidad",
3283
+ "rerunCount": "{attempts}/{max} reejecuciones",
3284
+ "exceeded": "Presupuesto agotado",
3285
+ "adequate": "Cobertura adecuada",
3286
+ "inadequate": "Se encontraron lagunas de cobertura",
3287
+ "gaps": "Lagunas por cerrar"
3288
+ },
3245
3289
  "empty": {
3246
3290
  "title": "Aún no hay informe de pruebas.",
3247
3291
  "hint": "El informe aparece cuando el Tester termina una pasada. Mientras se ejecuta, el paso muestra el progreso en vivo en el tablero."
@@ -3702,6 +3746,7 @@
3702
3746
  },
3703
3747
  "targetRepo": {
3704
3748
  "label": "Nombre del repositorio de destino",
3749
+ "namePlaceholder": "payments-service",
3705
3750
  "descWithOwner": "Crea un repositorio nuevo con este nombre bajo {owner} y luego la inicialización hace push a él. Un README, .gitignore o licencia ya creados están bien.",
3706
3751
  "descNoOwner": "Crea un repositorio nuevo con este nombre y luego la inicialización hace push a él. Un README, .gitignore o licencia ya creados están bien."
3707
3752
  },
@@ -3717,7 +3762,8 @@
3717
3762
  },
3718
3763
  "description": {
3719
3764
  "label": "Descripción",
3720
- "help": "Resumen opcional de una línea para el repositorio."
3765
+ "help": "Resumen opcional de una línea para el repositorio.",
3766
+ "placeholder": "Gestiona intenciones de pago y reembolsos"
3721
3767
  },
3722
3768
  "instructions": {
3723
3769
  "labelReference": "Instrucciones adicionales para el inicializador",
@@ -3747,18 +3793,23 @@
3747
3793
  "add": "Añadir",
3748
3794
  "pickRepo": {
3749
3795
  "label": "Elige un repositorio de GitHub existente",
3750
- "description": "Elige un repositorio al que tengas acceso para rellenar su propietario y nombre, o introdúcelos manualmente abajo."
3796
+ "description": "Elige un repositorio al que tengas acceso para rellenar su propietario y nombre, o introdúcelos manualmente abajo.",
3797
+ "placeholder": "owner/name"
3751
3798
  },
3752
3799
  "name": {
3753
3800
  "label": "Nombre",
3754
- "description": "Una etiqueta descriptiva para esta base."
3801
+ "description": "Una etiqueta descriptiva para esta base.",
3802
+ "placeholder": "Plantilla de servicio"
3755
3803
  },
3756
3804
  "repoOwner": "Propietario del repositorio",
3805
+ "repoOwnerPlaceholder": "acme",
3757
3806
  "repoName": "Nombre del repositorio",
3807
+ "repoNamePlaceholder": "service-template",
3758
3808
  "descriptionPlaceholder": "Resumen opcional de esta base",
3759
3809
  "defaultInstructions": {
3760
3810
  "label": "Instrucciones por defecto del inicializador",
3761
- "description": "Se anteponen a las instrucciones de cada ejecución siempre que se use esta base."
3811
+ "description": "Se anteponen a las instrucciones de cada ejecución siempre que se use esta base.",
3812
+ "placeholder": "p. ej. mantén la estructura; renombra los paquetes para que coincidan con el nuevo servicio"
3762
3813
  }
3763
3814
  },
3764
3815
  "toast": {
@@ -1,4 +1,10 @@
1
1
  {
2
+ "app": {
3
+ "loading": "Chargement…",
4
+ "loadingBoard": "Chargement du tableau…",
5
+ "backendUnreachable": "Impossible de joindre le backend",
6
+ "reconnecting": "Reconnexion…"
7
+ },
2
8
  "language": {
3
9
  "switcher": "Langue",
4
10
  "warning": {
@@ -68,6 +74,14 @@
68
74
  "infrastructure": "Infrastructure"
69
75
  },
70
76
  "board": {
77
+ "toast": {
78
+ "updateFailed": "Impossible d'enregistrer les modifications",
79
+ "epicFailed": "Impossible de changer l'épopée",
80
+ "moveFailed": "Impossible de déplacer",
81
+ "deleteFailed": "Impossible de supprimer",
82
+ "linkFailed": "Impossible de lier les tâches",
83
+ "recurringDeleteFailed": "Impossible de supprimer le pipeline récurrent"
84
+ },
71
85
  "repoTypes": {
72
86
  "service": "Service",
73
87
  "frontend": "Frontend",
@@ -260,6 +274,11 @@
260
274
  "openPrOnGithub": "Ouvrir {pr} sur GitHub",
261
275
  "review": "Revoir",
262
276
  "merge": "Fusionner",
277
+ "mergeConfirm": {
278
+ "title": "Fusionner cette pull request ?",
279
+ "body": "Cela fusionne la PR dans sa branche de base et termine la tâche. Cette action est irréversible.",
280
+ "confirm": "Fusionner"
281
+ },
263
282
  "implemented": "implémentée",
264
283
  "module": "Module : {name}",
265
284
  "buildSteps": "Étapes de build",
@@ -601,6 +620,16 @@
601
620
  "stopTooltip": "Arrêter l'exécution mais la conserver (lisible et relançable)",
602
621
  "reset": "Réinitialiser",
603
622
  "resetTooltip": "Abandonner cette exécution et réinitialiser la tâche à planifiée",
623
+ "resetConfirm": {
624
+ "title": "Abandonner cette exécution ?",
625
+ "body": "Cela supprime l'exécution et ramène la tâche à planifiée. Cette action est irréversible.",
626
+ "confirm": "Abandonner l'exécution"
627
+ },
628
+ "mergeConfirm": {
629
+ "title": "Fusionner cette pull request ?",
630
+ "body": "Cela fusionne la PR dans sa branche de base et termine la tâche. Cette action est irréversible.",
631
+ "confirm": "Fusionner"
632
+ },
604
633
  "viewDetailsOutput": "Voir les détails et lire la sortie",
605
634
  "viewDetails": "Voir les détails de l'étape",
606
635
  "companion": "Compagnon",
@@ -1200,7 +1229,9 @@
1200
1229
  },
1201
1230
  "toast": {
1202
1231
  "acted": "Marqué comme traité",
1203
- "dismissed": "Ignoré"
1232
+ "dismissed": "Ignoré",
1233
+ "actFailed": "Impossible de terminer cette action",
1234
+ "dismissFailed": "Impossible d'ignorer"
1204
1235
  }
1205
1236
  },
1206
1237
  "aiProvidersBanner": {
@@ -2601,6 +2632,10 @@
2601
2632
  "consensusRevertTooltip": "Consensus activé. Cliquez pour revenir à un seul agent.",
2602
2633
  "followUpEnableTooltip": "Compagnon de suivi désactivé. Cliquez pour l'activer (Coder repère les points en suspens / questions).",
2603
2634
  "followUpDisableTooltip": "Compagnon de suivi activé. Coder repère les points en suspens / sous-tâches / questions ; cliquez pour le désactiver.",
2635
+ "testerQualityEnableTooltip": "Compagnon de qualité des tests désactivé. Cliquez pour l'activer (audite la couverture du rapport et relance le Testeur en cas de lacunes).",
2636
+ "testerQualityDisableTooltip": "Compagnon de qualité des tests activé. Audite la couverture du rapport avant le feu vert et relance le Testeur en cas de lacunes ; cliquez pour le désactiver.",
2637
+ "testerQualityLabel": "Compagnon de qualité des tests",
2638
+ "testerQualityGateTooltip": "N'exécuter l'audit de qualité que lorsque l'estimation de la tâche dépasse un seuil (nécessite un Estimateur de tâches en amont)",
2604
2639
  "moveUp": "Monter l'étape",
2605
2640
  "moveDown": "Descendre l'étape",
2606
2641
  "removeStep": "Retirer cette étape du pipeline",
@@ -3242,6 +3277,15 @@
3242
3277
  "failed": "Échec",
3243
3278
  "addressed": "Corrige"
3244
3279
  },
3280
+ "quality": {
3281
+ "heading": "Revue de couverture",
3282
+ "reruns": "Relances du Testeur pilotées par la qualité",
3283
+ "rerunCount": "{attempts}/{max} relances",
3284
+ "exceeded": "Budget épuisé",
3285
+ "adequate": "Couverture adéquate",
3286
+ "inadequate": "Lacunes de couverture détectées",
3287
+ "gaps": "Lacunes à combler"
3288
+ },
3245
3289
  "empty": {
3246
3290
  "title": "Aucun rapport de tests pour l'instant.",
3247
3291
  "hint": "Le rapport apparaît une fois que le Testeur a terminé une passe. Pendant son exécution, l'étape affiche la progression en direct sur le tableau."
@@ -3702,6 +3746,7 @@
3702
3746
  },
3703
3747
  "targetRepo": {
3704
3748
  "label": "Nom du dépôt cible",
3749
+ "namePlaceholder": "payments-service",
3705
3750
  "descWithOwner": "Créez un nouveau dépôt portant ce nom sous {owner}, puis l'initialisation y pousse. Un README, un .gitignore ou une licence préremplis conviennent.",
3706
3751
  "descNoOwner": "Créez un nouveau dépôt portant ce nom, puis l'initialisation y pousse. Un README, un .gitignore ou une licence préremplis conviennent."
3707
3752
  },
@@ -3717,7 +3762,8 @@
3717
3762
  },
3718
3763
  "description": {
3719
3764
  "label": "Description",
3720
- "help": "Résumé facultatif en une ligne pour le dépôt."
3765
+ "help": "Résumé facultatif en une ligne pour le dépôt.",
3766
+ "placeholder": "Gère les intentions de paiement et les remboursements"
3721
3767
  },
3722
3768
  "instructions": {
3723
3769
  "labelReference": "Instructions supplémentaires pour l'initialiseur",
@@ -3747,18 +3793,23 @@
3747
3793
  "add": "Ajouter",
3748
3794
  "pickRepo": {
3749
3795
  "label": "Choisir un dépôt GitHub existant",
3750
- "description": "Choisissez un dépôt auquel vous avez accès pour renseigner son propriétaire et son nom, ou saisissez-les manuellement ci-dessous."
3796
+ "description": "Choisissez un dépôt auquel vous avez accès pour renseigner son propriétaire et son nom, ou saisissez-les manuellement ci-dessous.",
3797
+ "placeholder": "owner/name"
3751
3798
  },
3752
3799
  "name": {
3753
3800
  "label": "Nom",
3754
- "description": "Un libellé convivial pour cette base."
3801
+ "description": "Un libellé convivial pour cette base.",
3802
+ "placeholder": "Modèle de service"
3755
3803
  },
3756
3804
  "repoOwner": "Propriétaire du dépôt",
3805
+ "repoOwnerPlaceholder": "acme",
3757
3806
  "repoName": "Nom du dépôt",
3807
+ "repoNamePlaceholder": "service-template",
3758
3808
  "descriptionPlaceholder": "Résumé facultatif de cette base",
3759
3809
  "defaultInstructions": {
3760
3810
  "label": "Instructions d'initialisation par défaut",
3761
- "description": "Ajoutées avant les instructions de chaque exécution chaque fois que cette base est utilisée."
3811
+ "description": "Ajoutées avant les instructions de chaque exécution chaque fois que cette base est utilisée.",
3812
+ "placeholder": "p. ex. conserver la structure ; renommer les paquets pour correspondre au nouveau service"
3762
3813
  }
3763
3814
  },
3764
3815
  "toast": {