@cat-factory/app 0.217.2 → 0.219.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.
Files changed (33) hide show
  1. package/app/components/board/BoardCanvas.vue +15 -0
  2. package/app/components/board/nodes/TaskCard.vue +26 -11
  3. package/app/components/focus/BlockFocusView.vue +29 -1
  4. package/app/components/layout/AccountFailureKindRules.vue +285 -0
  5. package/app/components/layout/AccountPlatformAlertSettings.vue +43 -1
  6. package/app/components/panels/InspectorPanel.vue +49 -4
  7. package/app/components/panels/OperatorDashboardPanel.vue +7 -19
  8. package/app/components/panels/inspector/TaskExecution.vue +24 -2
  9. package/app/components/riskPolicy/RiskPolicyPreview.vue +33 -2
  10. package/app/components/settings/MergeRolePolicyEditor.logic.spec.ts +146 -0
  11. package/app/components/settings/MergeRolePolicyEditor.logic.ts +133 -0
  12. package/app/components/settings/MergeRolePolicyEditor.vue +226 -0
  13. package/app/components/settings/RiskPolicyPanel.vue +35 -1
  14. package/app/composables/useRunStart.spec.ts +204 -0
  15. package/app/composables/useRunStart.ts +112 -0
  16. package/app/stores/execution/commands.ts +20 -3
  17. package/app/types/execution.ts +1 -0
  18. package/app/types/merge.ts +6 -0
  19. package/app/utils/failureKinds.spec.ts +82 -0
  20. package/app/utils/failureKinds.ts +91 -0
  21. package/app/utils/riskPolicy.spec.ts +32 -1
  22. package/app/utils/riskPolicy.ts +29 -1
  23. package/i18n/locales/de.json +55 -5
  24. package/i18n/locales/en.json +61 -5
  25. package/i18n/locales/es.json +55 -5
  26. package/i18n/locales/fr.json +55 -5
  27. package/i18n/locales/he.json +55 -5
  28. package/i18n/locales/it.json +55 -5
  29. package/i18n/locales/ja.json +55 -5
  30. package/i18n/locales/pl.json +55 -5
  31. package/i18n/locales/tr.json +55 -5
  32. package/i18n/locales/uk.json +55 -5
  33. package/package.json +2 -2
@@ -1,6 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import type { RiskPolicy } from '~/types/merge'
3
- import { RISK_POLICY_AXES, RISK_POLICY_CEILING_FIELD, riskPolicyCeilings } from '~/utils/riskPolicy'
3
+ import {
4
+ RISK_POLICY_AXES,
5
+ RISK_POLICY_CEILING_FIELD,
6
+ riskPolicyCeilings,
7
+ rolePolicySummary,
8
+ } from '~/utils/riskPolicy'
4
9
 
5
10
  const policy = (over: Partial<RiskPolicy> = {}): RiskPolicy =>
6
11
  ({
@@ -20,6 +25,8 @@ const policy = (over: Partial<RiskPolicy> = {}): RiskPolicy =>
20
25
  judgeMaxBounces: 2,
21
26
  autoMergeEnabled: true,
22
27
  classRules: {},
28
+ classRulesByRole: {},
29
+ dryRunRoles: [],
23
30
  isDefault: true,
24
31
  version: 1,
25
32
  createdAt: 0,
@@ -51,3 +58,27 @@ describe('riskPolicyCeilings', () => {
51
58
  expect(Object.keys(RISK_POLICY_CEILING_FIELD).sort()).toEqual([...RISK_POLICY_AXES].sort())
52
59
  })
53
60
  })
61
+
62
+ describe('rolePolicySummary', () => {
63
+ it('is empty on a policy that treats every initiator alike', () => {
64
+ expect(rolePolicySummary(policy())).toEqual({ sandboxed: [], narrowed: [] })
65
+ })
66
+
67
+ it('lists both layers in the shared role order', () => {
68
+ const p = policy({
69
+ dryRunRoles: ['viewer', 'member'],
70
+ classRulesByRole: { admin: { schema: 'never' } },
71
+ })
72
+ expect(rolePolicySummary(p)).toEqual({
73
+ sandboxed: ['member', 'viewer'],
74
+ narrowed: ['admin'],
75
+ })
76
+ })
77
+
78
+ // The sandbox outranks the class rules, so naming both for one role would report a second
79
+ // limit that changes nothing about what that role's runs can do.
80
+ it('does not also report class rules for a role the policy sandboxes', () => {
81
+ const p = policy({ dryRunRoles: ['member'], classRulesByRole: { member: { docs: 'never' } } })
82
+ expect(rolePolicySummary(p)).toEqual({ sandboxed: ['member'], narrowed: [] })
83
+ })
84
+ })
@@ -1,4 +1,5 @@
1
- import type { RiskPolicy } from '~/types/merge'
1
+ import { WORKSPACE_ROLES } from '@cat-factory/contracts'
2
+ import type { RiskPolicy, WorkspaceRole } from '~/types/merge'
2
3
 
3
4
  /**
4
5
  * The three axes a `merger` agent scores a pull request on. Presentation order is
@@ -40,3 +41,30 @@ export interface RiskPolicyCeiling {
40
41
  export function riskPolicyCeilings(p: RiskPolicy): RiskPolicyCeiling[] {
41
42
  return RISK_POLICY_AXES.map((axis) => ({ axis, max: p[RISK_POLICY_CEILING_FIELD[axis]] }))
42
43
  }
44
+
45
+ /** What a policy's ROLE layer does, for the surfaces that explain a policy rather than edit it. */
46
+ export interface RolePolicySummary {
47
+ /** Roles whose runs are sandboxed: they open a pull request and merge nothing. */
48
+ sandboxed: WorkspaceRole[]
49
+ /**
50
+ * Roles held to stricter per-class rules than the base map. A sandboxed role is NOT listed
51
+ * here even when it also carries class rules: the sandbox already outranks them, so naming
52
+ * both would read as two limits where the second changes nothing.
53
+ */
54
+ narrowed: WorkspaceRole[]
55
+ }
56
+
57
+ /**
58
+ * A policy's role layer, in the shared role order so every surface names the tiers the same way.
59
+ * Both lists are empty on a policy that treats every initiator alike, which is every built-in, so
60
+ * a surface can render the whole section conditionally on that.
61
+ */
62
+ export function rolePolicySummary(p: RiskPolicy): RolePolicySummary {
63
+ const sandboxed = WORKSPACE_ROLES.filter((role) => p.dryRunRoles.includes(role))
64
+ return {
65
+ sandboxed: [...sandboxed],
66
+ narrowed: WORKSPACE_ROLES.filter(
67
+ (role) => !sandboxed.includes(role) && Object.keys(p.classRulesByRole[role] ?? {}).length > 0,
68
+ ),
69
+ }
70
+ }
@@ -522,6 +522,19 @@
522
522
  "always": "Immer automatisch zusammenführen",
523
523
  "never": "Immer Review verlangen"
524
524
  }
525
+ },
526
+ "roleRules": {
527
+ "heading": "Grenzen je startender Rolle",
528
+ "help": "Was ein Lauf darf, hängt auch davon ab, wer ihn gestartet hat. Eine Rolle kann strengeren Regeln als den obigen unterliegen oder nur Probeläufe machen: Die Pipeline arbeitet und öffnet einen Pull Request, zusammengeführt wird nichts. Eine Rolle kann diese Richtlinie nur verschärfen, nie lockern.",
529
+ "autoMergeOffWarning": "Für diese Vorlage ist das automatische Zusammenführen aus, jeder Pull Request geht also ohnehin an einen Menschen und die Kategoriegrenzen unten ändern nichts mehr. Probeläufe gelten weiterhin: Sie verweigern auch das manuelle Zusammenführen.",
530
+ "sandboxLabel": "Nur Probeläufe",
531
+ "sandboxNote": "Läufe dieser Rolle öffnen einen Pull Request und führen nichts zusammen, die Kategorieregeln unten können sie also nicht weiter einschränken.",
532
+ "noRules": "Keine Kategoriegrenzen",
533
+ "ruleCount": "{count} Kategorie eingeschränkt | {count} Kategorien eingeschränkt",
534
+ "inherit": "Wie in dieser Richtlinie",
535
+ "redundant": "Ohne Wirkung: Diese Richtlinie ist bereits mindestens so streng.",
536
+ "alreadyStrictest": "Wird ohnehin immer geprüft",
537
+ "baseHint": "Diese Richtlinie vergleicht in jeder Kategorie die Bewertungen, eine Rolle kann eine Kategorie daher nur auf \"immer Review\" verschärfen."
525
538
  }
526
539
  },
527
540
  "observabilityConnection": {
@@ -1161,7 +1174,28 @@
1161
1174
  "maxSweepFailures": "Aufeinanderfolgende fehlgeschlagene Durchläufe einer Hintergrundprüfung."
1162
1175
  },
1163
1176
  "notLoaded": "Die aktuellen Einstellungen dieses Kontos konnten nicht geladen werden; ein Speichern würde die übrigen überschreiben. Bitte vor dem Bearbeiten neu laden.",
1164
- "invalidNumbers": "Diese Obergrenzen sind keine Zahlen"
1177
+ "invalidNumbers": "Diese Obergrenzen sind keine Zahlen",
1178
+ "failureKinds": {
1179
+ "label": "Regeln pro Fehlerart",
1180
+ "description": "Alarm auslösen, wenn eine Fehlerart den Anteil an Fehlschlägen erreicht, den Sie ihr zugestehen. Die Obergrenze für die dominante Ursache oben fragt, ob eine Art alle anderen überlagert; diese Regeln fragen, ob eine benannte Art das erreicht, was diese Installation von ihr toleriert.",
1181
+ "overrideLabel": "Eigene Regeln pro Fehlerart für dieses Konto festlegen",
1182
+ "overrideHint": "Aus: Das Konto verwendet die Regeln der Installation. Ein: Die Liste unten ersetzt sie vollständig, und eine leere Liste bedeutet gar keine Regeln pro Fehlerart.",
1183
+ "empty": "Keine Regeln. Dieses Konto löst den Alarm pro Fehlerart nicht aus.",
1184
+ "kindLabel": "Fehlerart",
1185
+ "shareLabel": "Anteil an Fehlschlägen (%)",
1186
+ "shareHint": "Die Regel löst ab diesem Anteil aus.",
1187
+ "minCountLabel": "Mindestanzahl Fehlschläge",
1188
+ "minCountPlaceholder": "1",
1189
+ "minCountHint": "Wie viele Fehlschläge dieser Art im Zeitfenster nötig sind, bevor die Regel auslösen kann. Ohne diesen Wert ist ein niedriger Anteil überempfindlich: Fünf Läufe mit einer Verdrängung sind bereits 20 %.",
1190
+ "addRule": "Regel hinzufügen",
1191
+ "allKindsCovered": "Jede Fehlerart hat bereits eine Regel.",
1192
+ "removeRule": "Regel entfernen",
1193
+ "duplicateKind": "Jede Fehlerart darf höchstens eine Regel haben.",
1194
+ "tooManyRules": "Höchstens {max} Regeln pro Fehlerart. Entfernen Sie einige vor dem Speichern.",
1195
+ "unknownKind": "{kind} (unbekannt)",
1196
+ "invalidTitle": "Diese Regeln pro Fehlerart können nicht gespeichert werden",
1197
+ "invalidRows": "Prüfen Sie die Regeln {rows}: Der Anteil muss über 0 und höchstens 100 liegen, eine Mindestanzahl muss eine ganze Zahl ab 1 sein, und jede Art darf nur einmal vorkommen."
1198
+ }
1165
1199
  }
1166
1200
  },
1167
1201
  "inspector": {
@@ -1539,7 +1573,9 @@
1539
1573
  "chooseApproach": "Ansatz wählen",
1540
1574
  "triageFollowUps": "Folgeaufgaben entscheiden",
1541
1575
  "elapsedTooltip": "Verstrichene Zeit für diesen Schritt",
1542
- "reviewFindings": "Befunde prüfen"
1576
+ "reviewFindings": "Befunde prüfen",
1577
+ "dryRun": "Probelauf",
1578
+ "dryRunHint": "Dieser Lauf öffnet einen Pull Request und führt nichts zusammen."
1543
1579
  },
1544
1580
  "structure": {
1545
1581
  "title": "Struktur",
@@ -1960,7 +1996,10 @@
1960
1996
  "title": "Diesen Dienst archivieren?",
1961
1997
  "body": "„{name}“ und seine Aufgaben werden vom Board ausgeblendet. Du kannst den Dienst jederzeit wiederherstellen."
1962
1998
  },
1963
- "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}"
1999
+ "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}",
2000
+ "dryRun": "Probelauf: nichts zusammenführen",
2001
+ "dryRunForced": "Probelauf: Läufe deiner Rolle werden hier nie zusammengeführt",
2002
+ "dryRunHint": "Dieser Lauf öffnet einen Pull Request und führt nichts zusammen."
1964
2003
  }
1965
2004
  },
1966
2005
  "layout": {
@@ -2819,6 +2858,7 @@
2819
2858
  "start": "Starten",
2820
2859
  "blocked": "Blockiert",
2821
2860
  "startPipeline": "{name} starten",
2861
+ "startPipelineDryRun": "{name} als Probelauf starten: öffnet einen Pull Request, führt nichts zusammen",
2822
2862
  "pipelineFallback": "Pipeline",
2823
2863
  "waitingOn": "Wartet auf: {deps}",
2824
2864
  "blockedByDependenciesTitle": "Durch Abhängigkeiten blockiert",
@@ -2844,6 +2884,10 @@
2844
2884
  "reviewAndApprove": "Überprüfen & freigeben",
2845
2885
  "clickToViewStep": "Klicken, um Schrittdetails & Ausgabe anzuzeigen"
2846
2886
  },
2887
+ "dryRunToast": {
2888
+ "title": "Probelauf gestartet",
2889
+ "body": "\"{name}\" läuft. Läufe deiner Rolle führen bei dieser Aufgabe nichts zusammen."
2890
+ },
2847
2891
  "epic": {
2848
2892
  "label": "Epic",
2849
2893
  "activeCount": "{count} aktiv",
@@ -5779,7 +5823,10 @@
5779
5823
  "description": "Beschreibung",
5780
5824
  "overallProgress": "Gesamtfortschritt",
5781
5825
  "dependencies": "Abhängigkeiten",
5782
- "noDependencies": "Keine"
5826
+ "noDependencies": "Keine",
5827
+ "dryRun": "Probelauf",
5828
+ "dryRunHint": "Die Pipeline ausführen und einen Pull Request öffnen, aber nichts zusammenführen.",
5829
+ "dryRunForcedHint": "Die Risikorichtlinie dieser Aufgabe lässt deine Rolle nur Probeläufe machen."
5783
5830
  },
5784
5831
  "environments": {
5785
5832
  "title": "Ephemere Umgebung",
@@ -6046,7 +6093,10 @@
6046
6093
  "manualHeading": "Nur manuelle Prüfung",
6047
6094
  "manualExplainer": "Diese Richtlinie mergt nie von selbst, jeder Pull Request wartet also unabhängig von den Bewertungen auf eine menschliche Prüfung.",
6048
6095
  "ciHeading": "CI-Fixes",
6049
- "ciAttempts": "Der Lauf darf {count} Mal versuchen, eine rote CI grün zu bekommen, bevor er aufgibt. | Der Lauf darf {count} Mal versuchen, eine rote CI grün zu bekommen, bevor er aufgibt."
6096
+ "ciAttempts": "Der Lauf darf {count} Mal versuchen, eine rote CI grün zu bekommen, bevor er aufgibt. | Der Lauf darf {count} Mal versuchen, eine rote CI grün zu bekommen, bevor er aufgibt.",
6097
+ "roleHeading": "Wer den Lauf gestartet hat",
6098
+ "roleSandboxed": "Läufe von {roles} öffnen einen Pull Request und werden nie zusammengeführt.",
6099
+ "roleNarrowed": "Für Läufe von {roles} gelten in einigen Änderungskategorien strengere Regeln."
6050
6100
  }
6051
6101
  },
6052
6102
  "modelPreset": {
@@ -492,6 +492,7 @@
492
492
  "start": "Start",
493
493
  "blocked": "Blocked",
494
494
  "startPipeline": "Start {name}",
495
+ "startPipelineDryRun": "Start {name} as a dry run: opens a pull request, merges nothing",
495
496
  "pipelineFallback": "pipeline",
496
497
  "waitingOn": "Waiting on: {deps}",
497
498
  "blockedByDependenciesTitle": "Blocked by dependencies",
@@ -517,6 +518,13 @@
517
518
  "reviewAndApprove": "Review & approve",
518
519
  "clickToViewStep": "Click to view step details & output"
519
520
  },
521
+ "dryRunToast": {
522
+ "title": "Dry run started",
523
+ "body": "Running \"{name}\". Your role's runs merge nothing on this task."
524
+ },
525
+ "@dryRunToast": {
526
+ "description": "Toast shown when a run starts that the task's merge policy has sandboxed because of the initiator's workspace role: the pipeline runs and opens a pull request, and nothing merges. {name} is the pipeline name, kept verbatim. Not a choice the reader made, so word it as a statement of what the run will do rather than as a confirmation of a setting."
527
+ },
520
528
  "epic": {
521
529
  "label": "Epic",
522
530
  "activeCount": "{count} active",
@@ -1155,7 +1163,9 @@
1155
1163
  "chooseApproach": "Choose approach",
1156
1164
  "triageFollowUps": "Decide follow-ups",
1157
1165
  "elapsedTooltip": "Elapsed time on this step",
1158
- "reviewFindings": "Review findings"
1166
+ "reviewFindings": "Review findings",
1167
+ "dryRun": "Dry run",
1168
+ "dryRunHint": "This run opens a pull request and merges nothing."
1159
1169
  },
1160
1170
  "structure": {
1161
1171
  "title": "Structure",
@@ -1584,7 +1594,10 @@
1584
1594
  "runBlocked": "Blocked by an unfinished dependency: {names} | Blocked by {count} unfinished dependencies: {names}",
1585
1595
  "@runBlocked": {
1586
1596
  "description": "Count-driven plural: {count} unfinished dependencies, {names} their comma-joined titles. Languages with more than two plural forms (e.g. Polish, Ukrainian: one/few/many) need the extra pipe-separated forms."
1587
- }
1597
+ },
1598
+ "dryRun": "Dry run: merge nothing",
1599
+ "dryRunForced": "Dry run: your role's runs never merge here",
1600
+ "dryRunHint": "This run opens a pull request and merges nothing."
1588
1601
  }
1589
1602
  },
1590
1603
  "observability": {
@@ -2977,6 +2990,22 @@
2977
2990
  "always": "Always auto-merge",
2978
2991
  "never": "Always require review"
2979
2992
  }
2993
+ },
2994
+ "roleRules": {
2995
+ "heading": "Limits per initiator role",
2996
+ "help": "What a run may do also depends on who started it. A role can be held to stricter rules than the ones above, or to dry runs only: the pipeline works and opens a pull request, and nothing merges. A role can only narrow this policy, never widen it.",
2997
+ "autoMergeOffWarning": "Auto-merge is off for this preset, so every pull request already goes to a human and the class limits below add nothing. Dry runs still apply: they refuse a manual merge too.",
2998
+ "sandboxLabel": "Dry runs only",
2999
+ "sandboxNote": "Runs started by this role open a pull request and merge nothing, so the class rules below cannot hold them to anything stricter.",
3000
+ "noRules": "No class limits",
3001
+ "ruleCount": "{count} class limited | {count} classes limited",
3002
+ "@ruleCount": {
3003
+ "description": "Count-based tally of how many change classes this role narrows (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
3004
+ },
3005
+ "inherit": "Same as this policy",
3006
+ "redundant": "No effect: this policy is already at least as strict.",
3007
+ "alreadyStrictest": "Already always reviewed",
3008
+ "baseHint": "This policy compares the scores for every class, so a role can only narrow a class to review-only."
2980
3009
  }
2981
3010
  },
2982
3011
  "observabilityConnection": {
@@ -3619,7 +3648,28 @@
3619
3648
  "maxSweepFailures": "Consecutive failed passes of one background sweep."
3620
3649
  },
3621
3650
  "notLoaded": "This account's current settings could not be loaded, so saving would overwrite the rest of them. Reload before editing.",
3622
- "invalidNumbers": "These ceilings are not numbers"
3651
+ "invalidNumbers": "These ceilings are not numbers",
3652
+ "failureKinds": {
3653
+ "label": "Per-failure-kind rules",
3654
+ "description": "Page when one failure kind reaches the share of failures you allow it. The dominant-cause ceiling above asks whether one kind is swamping the rest; these ask whether a named kind has reached what this deployment tolerates from it.",
3655
+ "overrideLabel": "Set this account's own per-kind rules",
3656
+ "overrideHint": "Off, the account uses the rules the deployment configured. On, the list below replaces them entirely, and an empty list means no per-kind rules at all.",
3657
+ "empty": "No rules. This account will not raise the per-kind alert.",
3658
+ "kindLabel": "Failure kind",
3659
+ "shareLabel": "Share of failures (%)",
3660
+ "shareHint": "The rule fires at this share or above.",
3661
+ "minCountLabel": "Minimum failures",
3662
+ "minCountPlaceholder": "1",
3663
+ "minCountHint": "How many failures of that kind the window needs before the rule can fire. A low share is a hair trigger without it: five runs with one eviction is already 20%.",
3664
+ "addRule": "Add rule",
3665
+ "allKindsCovered": "Every failure kind already has a rule.",
3666
+ "removeRule": "Remove rule",
3667
+ "duplicateKind": "Each failure kind may carry at most one rule.",
3668
+ "tooManyRules": "At most {max} per-kind rules. Remove some before saving.",
3669
+ "unknownKind": "{kind} (unrecognised)",
3670
+ "invalidTitle": "These per-kind rules cannot be saved",
3671
+ "invalidRows": "Check rules {rows}: the share must be above 0 and at most 100, a minimum count must be a whole number of 1 or more, and each kind may appear once."
3672
+ }
3623
3673
  }
3624
3674
  },
3625
3675
  "providers": {
@@ -4634,7 +4684,10 @@
4634
4684
  "manualHeading": "Manual review only",
4635
4685
  "manualExplainer": "This policy never merges on its own, so every pull request waits for a human review whatever it scores.",
4636
4686
  "ciHeading": "CI fixes",
4637
- "ciAttempts": "The run may try {count} time to turn a red CI green before it gives up. | The run may try {count} times to turn a red CI green before it gives up."
4687
+ "ciAttempts": "The run may try {count} time to turn a red CI green before it gives up. | The run may try {count} times to turn a red CI green before it gives up.",
4688
+ "roleHeading": "Who started the run",
4689
+ "roleSandboxed": "Runs started by {roles} open a pull request and never merge.",
4690
+ "roleNarrowed": "Runs started by {roles} follow stricter rules for some change classes."
4638
4691
  }
4639
4692
  },
4640
4693
  "modelPreset": {
@@ -5463,7 +5516,10 @@
5463
5516
  "description": "Description",
5464
5517
  "overallProgress": "Overall progress",
5465
5518
  "dependencies": "Dependencies",
5466
- "noDependencies": "None"
5519
+ "noDependencies": "None",
5520
+ "dryRun": "Dry run",
5521
+ "dryRunHint": "Run the pipeline and open a pull request, but merge nothing.",
5522
+ "dryRunForcedHint": "This task's risk policy holds your role to dry runs."
5467
5523
  },
5468
5524
  "environments": {
5469
5525
  "title": "Ephemeral environment",
@@ -447,6 +447,7 @@
447
447
  "start": "Iniciar",
448
448
  "blocked": "Bloqueada",
449
449
  "startPipeline": "Iniciar {name}",
450
+ "startPipelineDryRun": "Iniciar {name} como ejecución de prueba: abre una pull request y no fusiona nada",
450
451
  "pipelineFallback": "pipeline",
451
452
  "waitingOn": "Esperando a: {deps}",
452
453
  "blockedByDependenciesTitle": "Bloqueada por dependencias",
@@ -472,6 +473,10 @@
472
473
  "body": "Ejecutando \"{name}\"."
473
474
  }
474
475
  },
476
+ "dryRunToast": {
477
+ "title": "Ejecución de prueba iniciada",
478
+ "body": "Ejecutando \"{name}\". Las ejecuciones de tu rol no fusionan nada en esta tarea."
479
+ },
475
480
  "epic": {
476
481
  "label": "Épica",
477
482
  "activeCount": "{count} activas",
@@ -1077,7 +1082,9 @@
1077
1082
  "chooseApproach": "Elegir enfoque",
1078
1083
  "triageFollowUps": "Decidir seguimientos",
1079
1084
  "elapsedTooltip": "Tiempo transcurrido en este paso",
1080
- "reviewFindings": "Revisar hallazgos"
1085
+ "reviewFindings": "Revisar hallazgos",
1086
+ "dryRun": "Ejecución de prueba",
1087
+ "dryRunHint": "Esta ejecución abre una pull request y no fusiona nada."
1081
1088
  },
1082
1089
  "structure": {
1083
1090
  "title": "Estructura",
@@ -1498,7 +1505,10 @@
1498
1505
  "title": "¿Archivar este servicio?",
1499
1506
  "body": "«{name}» y sus tareas se ocultarán del tablero. Puedes restaurarlo en cualquier momento."
1500
1507
  },
1501
- "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}"
1508
+ "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}",
1509
+ "dryRun": "Ejecución de prueba: no fusionar nada",
1510
+ "dryRunForced": "Ejecución de prueba: las ejecuciones de tu rol nunca se fusionan aquí",
1511
+ "dryRunHint": "Esta ejecución abre una pull request y no fusiona nada."
1502
1512
  }
1503
1513
  },
1504
1514
  "observability": {
@@ -2738,6 +2748,19 @@
2738
2748
  "always": "Fusionar siempre automáticamente",
2739
2749
  "never": "Exigir siempre revisión"
2740
2750
  }
2751
+ },
2752
+ "roleRules": {
2753
+ "heading": "Límites según el rol que inicia",
2754
+ "help": "Lo que puede hacer una ejecución también depende de quién la inició. Un rol puede quedar sujeto a reglas más estrictas que las de arriba, o solo a ejecuciones de prueba: la canalización trabaja y abre una pull request, pero no se fusiona nada. Un rol solo puede endurecer esta política, nunca relajarla.",
2755
+ "autoMergeOffWarning": "El auto-merge está desactivado en este preajuste, así que cada pull request ya pasa por una persona y los límites de categoría de abajo no añaden nada. Las ejecuciones de prueba siguen aplicándose: también rechazan una fusión manual.",
2756
+ "sandboxLabel": "Solo ejecuciones de prueba",
2757
+ "sandboxNote": "Las ejecuciones iniciadas por este rol abren una pull request y no fusionan nada, así que las reglas por categoría de abajo no pueden restringirlas más.",
2758
+ "noRules": "Sin límites por categoría",
2759
+ "ruleCount": "{count} categoría limitada | {count} categorías limitadas",
2760
+ "inherit": "Igual que esta política",
2761
+ "redundant": "Sin efecto: esta política ya es igual de estricta o más.",
2762
+ "alreadyStrictest": "Ya se revisa siempre",
2763
+ "baseHint": "Esta política compara las puntuaciones en todas las categorías, así que un rol solo puede endurecer una categoría hasta exigir revisión."
2741
2764
  }
2742
2765
  },
2743
2766
  "observabilityConnection": {
@@ -3518,7 +3541,28 @@
3518
3541
  "maxSweepFailures": "Pasadas fallidas consecutivas de un barrido en segundo plano."
3519
3542
  },
3520
3543
  "notLoaded": "No se pudieron cargar los ajustes actuales de esta cuenta, así que guardar sobrescribiría el resto. Vuelve a cargar antes de editar.",
3521
- "invalidNumbers": "Estos límites no son números"
3544
+ "invalidNumbers": "Estos límites no son números",
3545
+ "failureKinds": {
3546
+ "label": "Reglas por tipo de fallo",
3547
+ "description": "Avisa cuando un tipo de fallo alcanza la proporción de fallos que le permites. El límite de causa dominante de arriba pregunta si un tipo está desbordando al resto; estas reglas preguntan si un tipo concreto ha alcanzado lo que este despliegue tolera de él.",
3548
+ "overrideLabel": "Definir reglas por tipo propias de esta cuenta",
3549
+ "overrideHint": "Desactivado, la cuenta usa las reglas configuradas en el despliegue. Activado, la lista de abajo las sustituye por completo, y una lista vacía significa ninguna regla por tipo.",
3550
+ "empty": "Sin reglas. Esta cuenta no generará la alerta por tipo de fallo.",
3551
+ "kindLabel": "Tipo de fallo",
3552
+ "shareLabel": "Proporción de fallos (%)",
3553
+ "shareHint": "La regla se activa a partir de esta proporción.",
3554
+ "minCountLabel": "Fallos mínimos",
3555
+ "minCountPlaceholder": "1",
3556
+ "minCountHint": "Cuántos fallos de ese tipo necesita la ventana antes de que la regla pueda dispararse. Sin esto, una proporción baja salta a la mínima: cinco ejecuciones con un desalojo ya son el 20 %.",
3557
+ "addRule": "Añadir regla",
3558
+ "allKindsCovered": "Cada tipo de fallo ya tiene una regla.",
3559
+ "removeRule": "Eliminar regla",
3560
+ "duplicateKind": "Cada tipo de fallo admite como máximo una regla.",
3561
+ "tooManyRules": "Como máximo {max} reglas por tipo. Elimina algunas antes de guardar.",
3562
+ "unknownKind": "{kind} (no reconocido)",
3563
+ "invalidTitle": "Estas reglas por tipo no se pueden guardar",
3564
+ "invalidRows": "Revisa las reglas {rows}: la proporción debe ser mayor que 0 y como máximo 100, el mínimo debe ser un número entero de 1 o más, y cada tipo puede aparecer una sola vez."
3565
+ }
3522
3566
  }
3523
3567
  },
3524
3568
  "providers": {
@@ -5224,7 +5268,10 @@
5224
5268
  "description": "Descripción",
5225
5269
  "overallProgress": "Progreso general",
5226
5270
  "dependencies": "Dependencias",
5227
- "noDependencies": "Ninguna"
5271
+ "noDependencies": "Ninguna",
5272
+ "dryRun": "Ejecución de prueba",
5273
+ "dryRunHint": "Ejecutar la canalización y abrir una pull request, pero no fusionar nada.",
5274
+ "dryRunForcedHint": "La política de riesgo de esta tarea limita tu rol a ejecuciones de prueba."
5228
5275
  },
5229
5276
  "environments": {
5230
5277
  "title": "Entorno efímero",
@@ -5913,7 +5960,10 @@
5913
5960
  "manualHeading": "Solo revisión manual",
5914
5961
  "manualExplainer": "Esta política nunca fusiona por su cuenta, así que cada pull request espera una revisión humana sea cual sea su puntuación.",
5915
5962
  "ciHeading": "Correcciones de CI",
5916
- "ciAttempts": "La ejecución puede intentar {count} vez poner CI en verde antes de rendirse. | La ejecución puede intentar {count} veces poner CI en verde antes de rendirse."
5963
+ "ciAttempts": "La ejecución puede intentar {count} vez poner CI en verde antes de rendirse. | La ejecución puede intentar {count} veces poner CI en verde antes de rendirse.",
5964
+ "roleHeading": "Quién inició la ejecución",
5965
+ "roleSandboxed": "Las ejecuciones iniciadas por {roles} abren una pull request y nunca se fusionan.",
5966
+ "roleNarrowed": "Las ejecuciones iniciadas por {roles} siguen reglas más estrictas en algunas categorías de cambio."
5917
5967
  }
5918
5968
  },
5919
5969
  "modelPreset": {
@@ -447,6 +447,7 @@
447
447
  "start": "Démarrer",
448
448
  "blocked": "Bloquée",
449
449
  "startPipeline": "Démarrer {name}",
450
+ "startPipelineDryRun": "Démarrer {name} en exécution à blanc : ouvre une pull request, ne fusionne rien",
450
451
  "pipelineFallback": "pipeline",
451
452
  "waitingOn": "En attente de : {deps}",
452
453
  "blockedByDependenciesTitle": "Bloquée par des dépendances",
@@ -472,6 +473,10 @@
472
473
  "body": "Exécution de \"{name}\"."
473
474
  }
474
475
  },
476
+ "dryRunToast": {
477
+ "title": "Exécution à blanc démarrée",
478
+ "body": "Exécution de \"{name}\". Les exécutions de ton rôle ne fusionnent rien sur cette tâche."
479
+ },
475
480
  "epic": {
476
481
  "label": "Épopée",
477
482
  "activeCount": "{count} actives",
@@ -1077,7 +1082,9 @@
1077
1082
  "chooseApproach": "Choisir l'approche",
1078
1083
  "triageFollowUps": "Trancher les suivis",
1079
1084
  "elapsedTooltip": "Temps écoulé sur cette étape",
1080
- "reviewFindings": "Examiner les remarques"
1085
+ "reviewFindings": "Examiner les remarques",
1086
+ "dryRun": "Exécution à blanc",
1087
+ "dryRunHint": "Cette exécution ouvre une pull request et ne fusionne rien."
1081
1088
  },
1082
1089
  "structure": {
1083
1090
  "title": "Structure",
@@ -1498,7 +1505,10 @@
1498
1505
  "title": "Archiver ce service ?",
1499
1506
  "body": "« {name} » et ses tâches seront masqués du tableau. Vous pouvez le restaurer à tout moment."
1500
1507
  },
1501
- "runBlocked": "Bloqué par une dépendance non terminée : {names} | Bloqué par {count} dépendances non terminées : {names}"
1508
+ "runBlocked": "Bloqué par une dépendance non terminée : {names} | Bloqué par {count} dépendances non terminées : {names}",
1509
+ "dryRun": "Exécution à blanc : ne rien fusionner",
1510
+ "dryRunForced": "Exécution à blanc : les exécutions de votre rôle ne sont jamais fusionnées ici",
1511
+ "dryRunHint": "Cette exécution ouvre une pull request et ne fusionne rien."
1502
1512
  }
1503
1513
  },
1504
1514
  "observability": {
@@ -2738,6 +2748,19 @@
2738
2748
  "always": "Toujours fusionner automatiquement",
2739
2749
  "never": "Toujours exiger une revue"
2740
2750
  }
2751
+ },
2752
+ "roleRules": {
2753
+ "heading": "Limites selon le rôle qui lance l'exécution",
2754
+ "help": "Ce qu'une exécution peut faire dépend aussi de qui l'a lancée. Un rôle peut être soumis à des règles plus strictes que celles ci-dessus, ou aux seules exécutions à blanc : le pipeline travaille et ouvre une pull request, mais rien n'est fusionné. Un rôle peut seulement durcir cette politique, jamais l'assouplir.",
2755
+ "autoMergeOffWarning": "La fusion automatique est désactivée pour ce préréglage : chaque pull request passe déjà par un humain et les limites de catégorie ci-dessous n'ajoutent rien. Les exécutions à blanc restent en vigueur : elles refusent aussi une fusion manuelle.",
2756
+ "sandboxLabel": "Exécutions à blanc uniquement",
2757
+ "sandboxNote": "Les exécutions lancées par ce rôle ouvrent une pull request et ne fusionnent rien : les règles par catégorie ci-dessous ne peuvent donc rien durcir de plus.",
2758
+ "noRules": "Aucune limite par catégorie",
2759
+ "ruleCount": "{count} catégorie limitée | {count} catégories limitées",
2760
+ "inherit": "Comme cette politique",
2761
+ "redundant": "Sans effet : cette politique est déjà au moins aussi stricte.",
2762
+ "alreadyStrictest": "Déjà toujours relu",
2763
+ "baseHint": "Cette politique compare les scores pour chaque catégorie : un rôle ne peut donc durcir une catégorie que jusqu'à la revue obligatoire."
2741
2764
  }
2742
2765
  },
2743
2766
  "observabilityConnection": {
@@ -3518,7 +3541,28 @@
3518
3541
  "maxSweepFailures": "Passages consécutifs en échec d'un balayage en arrière-plan."
3519
3542
  },
3520
3543
  "notLoaded": "Les paramètres actuels de ce compte n'ont pas pu être chargés ; enregistrer écraserait les autres. Rechargez avant de modifier.",
3521
- "invalidNumbers": "Ces plafonds ne sont pas des nombres"
3544
+ "invalidNumbers": "Ces plafonds ne sont pas des nombres",
3545
+ "failureKinds": {
3546
+ "label": "Règles par type d'échec",
3547
+ "description": "Alerter quand un type d'échec atteint la part d'échecs que vous lui accordez. Le plafond de cause dominante ci-dessus demande si un type submerge les autres ; ces règles demandent si un type nommé a atteint ce que ce déploiement tolère de lui.",
3548
+ "overrideLabel": "Définir les règles par type propres à ce compte",
3549
+ "overrideHint": "Désactivé, le compte utilise les règles configurées par le déploiement. Activé, la liste ci-dessous les remplace entièrement, et une liste vide signifie aucune règle par type.",
3550
+ "empty": "Aucune règle. Ce compte ne déclenchera pas l'alerte par type d'échec.",
3551
+ "kindLabel": "Type d'échec",
3552
+ "shareLabel": "Part des échecs (%)",
3553
+ "shareHint": "La règle se déclenche à partir de cette part.",
3554
+ "minCountLabel": "Échecs minimum",
3555
+ "minCountPlaceholder": "1",
3556
+ "minCountHint": "Combien d'échecs de ce type la fenêtre doit compter avant que la règle puisse se déclencher. Sans cela, une part faible se déclenche pour un rien : cinq exécutions avec une éviction, c'est déjà 20 %.",
3557
+ "addRule": "Ajouter une règle",
3558
+ "allKindsCovered": "Chaque type d'échec a déjà une règle.",
3559
+ "removeRule": "Supprimer la règle",
3560
+ "duplicateKind": "Chaque type d'échec ne peut porter qu'une seule règle.",
3561
+ "tooManyRules": "Au plus {max} règles par type. Supprimez-en avant d’enregistrer.",
3562
+ "unknownKind": "{kind} (non reconnu)",
3563
+ "invalidTitle": "Ces règles par type ne peuvent pas être enregistrées",
3564
+ "invalidRows": "Vérifiez les règles {rows} : la part doit être supérieure à 0 et au plus 100, un minimum doit être un entier supérieur ou égal à 1, et chaque type ne peut apparaître qu'une fois."
3565
+ }
3522
3566
  }
3523
3567
  },
3524
3568
  "providers": {
@@ -5224,7 +5268,10 @@
5224
5268
  "description": "Description",
5225
5269
  "overallProgress": "Progression globale",
5226
5270
  "dependencies": "Dépendances",
5227
- "noDependencies": "Aucune"
5271
+ "noDependencies": "Aucune",
5272
+ "dryRun": "Exécution à blanc",
5273
+ "dryRunHint": "Lancer le pipeline et ouvrir une pull request, sans rien fusionner.",
5274
+ "dryRunForcedHint": "La politique de risque de cette tâche limite votre rôle aux exécutions à blanc."
5228
5275
  },
5229
5276
  "environments": {
5230
5277
  "title": "Environnement éphémère",
@@ -5913,7 +5960,10 @@
5913
5960
  "manualHeading": "Revue manuelle uniquement",
5914
5961
  "manualExplainer": "Cette politique ne fusionne jamais d'elle-même : chaque pull request attend une revue humaine, quelles que soient les notes.",
5915
5962
  "ciHeading": "Corrections CI",
5916
- "ciAttempts": "L'exécution peut tenter {count} fois de remettre la CI au vert avant d'abandonner. | L'exécution peut tenter {count} fois de remettre la CI au vert avant d'abandonner."
5963
+ "ciAttempts": "L'exécution peut tenter {count} fois de remettre la CI au vert avant d'abandonner. | L'exécution peut tenter {count} fois de remettre la CI au vert avant d'abandonner.",
5964
+ "roleHeading": "Qui a lancé l'exécution",
5965
+ "roleSandboxed": "Les exécutions lancées par {roles} ouvrent une pull request et ne sont jamais fusionnées.",
5966
+ "roleNarrowed": "Les exécutions lancées par {roles} suivent des règles plus strictes pour certaines catégories de changement."
5917
5967
  }
5918
5968
  },
5919
5969
  "modelPreset": {