@cat-factory/app 0.314.0 → 0.315.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.
@@ -0,0 +1,179 @@
1
+ import type { ExecutionInstance, Notification, PipelineStep } from '~/types/domain'
2
+
3
+ /**
4
+ * Where each notification takes a person, and the verb that names it (issue #2258, finding F4).
5
+ *
6
+ * ONE row per type holds both, so the button's word and the place it opens cannot drift apart:
7
+ * the inbox's title link and its primary button both dispatch through {@link notificationTarget},
8
+ * and the verb is decided from the SAME resolution. A route that can only fall back to selecting
9
+ * the task (its run is not loaded, or not parked) is named "Open", because that is what happens.
10
+ *
11
+ * Exhaustive over the notification types, so a new type has to decide. A `null` verb means the
12
+ * card's own act button already IS the action (Merge, Retry run, Acknowledge), or there is nowhere
13
+ * to go beyond the card itself.
14
+ */
15
+
16
+ /** The store reads and window openers a route needs, injected so this stays testable. */
17
+ export interface NotificationRouteDeps {
18
+ getInstance: (id: string) => ExecutionInstance | undefined
19
+ select: (blockId: string) => void
20
+ openStepDetail: (instanceId: string, stepIndex: number) => void
21
+ openDecision: (instanceId: string, decisionId: string) => void
22
+ openFollowUps: (instanceId: string) => void
23
+ openForkDecision: (instanceId: string) => void
24
+ openPrReview: (instanceId: string) => void
25
+ openBugFishing: (instanceId: string) => void
26
+ openRequirementReview: (blockId: string) => void
27
+ openClarityReview: (blockId: string) => void
28
+ openInitiativeTracker: (blockId: string) => void
29
+ openOperatorDashboard: () => void
30
+ }
31
+
32
+ /**
33
+ * Where a click lands. `surface` opens the window that answers the card; `task` only selects the
34
+ * task on the board, which is the fallback when that window cannot open.
35
+ */
36
+ export interface NotificationTarget {
37
+ readonly kind: 'surface' | 'task'
38
+ readonly open: () => void
39
+ }
40
+
41
+ interface NotificationRoute {
42
+ /** The verb for a `surface` target, or null when the card's act button is already the verb. */
43
+ readonly verbKey: string | null
44
+ readonly resolve: (n: Notification, deps: NotificationRouteDeps) => NotificationTarget | null
45
+ }
46
+
47
+ /** The verb for a route that can only select the task. */
48
+ export const OPEN_TASK_VERB_KEY = 'layout.notifications.goTo.open'
49
+
50
+ const surface = (open: () => void): NotificationTarget => ({ kind: 'surface', open })
51
+
52
+ /** Select the card's task: the fallback every block-bound route shares. */
53
+ function selectTask(n: Notification, deps: NotificationRouteDeps): NotificationTarget | null {
54
+ const blockId = n.blockId
55
+ return blockId ? { kind: 'task', open: () => deps.select(blockId) } : null
56
+ }
57
+
58
+ /** A window keyed by the card's run, when that run is loaded; else the task. */
59
+ function byRun(open: (deps: NotificationRouteDeps, instanceId: string) => void) {
60
+ return (n: Notification, deps: NotificationRouteDeps): NotificationTarget | null => {
61
+ const id = n.executionId
62
+ if (id && deps.getInstance(id)) return surface(() => open(deps, id))
63
+ return selectTask(n, deps)
64
+ }
65
+ }
66
+
67
+ /** The run's step matching `find`, opened through the universal step dispatch; else the task. */
68
+ function byStep(find: (step: PipelineStep) => boolean) {
69
+ return (n: Notification, deps: NotificationRouteDeps): NotificationTarget | null => {
70
+ const instance = n.executionId ? deps.getInstance(n.executionId) : undefined
71
+ const index = instance?.steps.findIndex(find) ?? -1
72
+ if (instance && index >= 0) return surface(() => deps.openStepDetail(instance.id, index))
73
+ return selectTask(n, deps)
74
+ }
75
+ }
76
+
77
+ /** A window keyed by the card's block. */
78
+ function byBlock(open: (deps: NotificationRouteDeps, blockId: string) => void) {
79
+ return (n: Notification, deps: NotificationRouteDeps): NotificationTarget | null => {
80
+ const blockId = n.blockId
81
+ return blockId ? surface(() => open(deps, blockId)) : null
82
+ }
83
+ }
84
+
85
+ /**
86
+ * A parked step: an agent's question opens the decision window, where it is answered (as from the
87
+ * task card and the toolbar); any other park (an approval gate, an iteration cap) opens the step.
88
+ * A card whose run is not parked, which a Ralph loop or an exhausted gate also raises, opens the
89
+ * task.
90
+ */
91
+ function parkedStep(n: Notification, deps: NotificationRouteDeps): NotificationTarget | null {
92
+ const instance = n.executionId ? deps.getInstance(n.executionId) : undefined
93
+ const index = instance?.steps.findIndex((s) => s.state === 'waiting_decision') ?? -1
94
+ const step = instance?.steps[index]
95
+ if (!instance || !step) return selectTask(n, deps)
96
+ const decision = step.decision
97
+ if (decision && !decision.chosen) {
98
+ return surface(() => deps.openDecision(instance.id, decision.id))
99
+ }
100
+ return surface(() => deps.openStepDetail(instance.id, index))
101
+ }
102
+
103
+ const verb = (leaf: string) => `layout.notifications.goTo.${leaf}`
104
+
105
+ export const NOTIFICATION_ROUTES: Record<Notification['type'], NotificationRoute> = {
106
+ merge_review: { verbKey: null, resolve: selectTask },
107
+ pipeline_complete: { verbKey: null, resolve: selectTask },
108
+ merge_tag_request: { verbKey: null, resolve: selectTask },
109
+ ci_failed: { verbKey: null, resolve: selectTask },
110
+ test_failed: { verbKey: null, resolve: selectTask },
111
+ deploy_blocked: { verbKey: null, resolve: selectTask },
112
+ release_regression: { verbKey: null, resolve: selectTask },
113
+ infra_unreachable: { verbKey: null, resolve: selectTask },
114
+ key_drift: { verbKey: null, resolve: selectTask },
115
+ // Raised with no block, so neither has anywhere to go beyond the card.
116
+ budget_threshold: { verbKey: null, resolve: selectTask },
117
+ budget_paused: { verbKey: null, resolve: selectTask },
118
+ // Deployment-scoped: the dashboard holds the live numbers behind the alert.
119
+ platform_health: { verbKey: null, resolve: (_n, deps) => surface(deps.openOperatorDashboard) },
120
+ requirement_review: {
121
+ verbKey: verb('review'),
122
+ resolve: byBlock((deps, id) => deps.openRequirementReview(id)),
123
+ },
124
+ clarity_review: {
125
+ verbKey: verb('review'),
126
+ resolve: byBlock((deps, id) => deps.openClarityReview(id)),
127
+ },
128
+ decision_required: { verbKey: verb('answer'), resolve: parkedStep },
129
+ human_test_ready: {
130
+ verbKey: verb('test'),
131
+ resolve: byStep((s) => s.agentKind === 'human-test' && s.state === 'waiting_decision'),
132
+ },
133
+ visual_confirmation_ready: {
134
+ verbKey: verb('confirm'),
135
+ resolve: byStep((s) => s.agentKind === 'visual-confirmation' && s.state === 'waiting_decision'),
136
+ },
137
+ human_review: { verbKey: verb('review'), resolve: byStep((s) => s.agentKind === 'human-review') },
138
+ judge_review: {
139
+ verbKey: verb('review'),
140
+ resolve: byStep((s) => s.judge?.status === 'awaiting_decision'),
141
+ },
142
+ followup_pending: {
143
+ verbKey: verb('triage'),
144
+ resolve: byRun((deps, id) => deps.openFollowUps(id)),
145
+ },
146
+ fork_decision_pending: {
147
+ verbKey: verb('choose'),
148
+ resolve: byRun((deps, id) => deps.openForkDecision(id)),
149
+ },
150
+ pr_review_ready: { verbKey: verb('review'), resolve: byRun((deps, id) => deps.openPrReview(id)) },
151
+ bug_fishing_triage: {
152
+ verbKey: verb('triage'),
153
+ resolve: byRun((deps, id) => deps.openBugFishing(id)),
154
+ },
155
+ initiative: {
156
+ verbKey: verb('open'),
157
+ resolve: byBlock((deps, id) => deps.openInitiativeTracker(id)),
158
+ },
159
+ }
160
+
161
+ /** Where a click on this card lands, or null when it has nowhere to go. */
162
+ export function notificationTarget(
163
+ n: Notification,
164
+ deps: NotificationRouteDeps,
165
+ ): NotificationTarget | null {
166
+ return NOTIFICATION_ROUTES[n.type].resolve(n, deps)
167
+ }
168
+
169
+ /**
170
+ * The primary button's verb, from the same resolution the click takes: the route's verb when its
171
+ * window can open, "Open" when only the task can, null when the card has no verb or nowhere to go.
172
+ */
173
+ export function notificationVerbKey(n: Notification, deps: NotificationRouteDeps): string | null {
174
+ const route = NOTIFICATION_ROUTES[n.type]
175
+ if (!route.verbKey) return null
176
+ const target = route.resolve(n, deps)
177
+ if (!target) return null
178
+ return target.kind === 'surface' ? route.verbKey : OPEN_TASK_VERB_KEY
179
+ }
@@ -842,7 +842,9 @@
842
842
  "createdByKey": "API-Schlüssel {id}",
843
843
  "revoke": "Token widerrufen",
844
844
  "boundToYou": "Ihr Abonnement",
845
- "boundToOther": "Abonnement von {user}"
845
+ "boundToOther": "Abonnement von {user}",
846
+ "reachAll": "Alle Arbeitsbereiche",
847
+ "reachCount": "{count} Arbeitsbereiche"
846
848
  },
847
849
  "add": {
848
850
  "heading": "Token erstellen",
@@ -856,7 +858,13 @@
856
858
  "identitySystem": "Dieser Arbeitsbereich (System-Token)",
857
859
  "identitySelf": "Ich (persönliches Token)",
858
860
  "identitySystemHelp": "Mit diesem Token gestartete Läufe gehören dem Arbeitsbereich und werden keiner Person zugeordnet. Es kann kein persönliches Claude-/Codex-/GLM-Abonnement nutzen: Eine Aufgabe mit einem solchen Modell wird abgelehnt, statt jemandem in Abwesenheit berechnet zu werden. Für CI und gemeinsam genutzte Integrationen.",
859
- "identitySelfHelp": "Mit diesem Token gestartete Läufe gelten als Ihre und können Ihr persönliches Claude-/Codex-/GLM-Abonnement nutzen. Jeder solche Aufruf muss zusätzlich Ihr persönliches Passwort im Header X-Personal-Password senden; es wird nie gespeichert. Für Ihre eigenen Headless-Läufe."
861
+ "identitySelfHelp": "Mit diesem Token gestartete Läufe gelten als Ihre und können Ihr persönliches Claude-/Codex-/GLM-Abonnement nutzen. Jeder solche Aufruf muss zusätzlich Ihr persönliches Passwort im Header X-Personal-Password senden; es wird nie gespeichert. Für Ihre eigenen Headless-Läufe.",
862
+ "reach": "Arbeitsbereiche",
863
+ "reachHelp": "Auf welchen Arbeitsbereichen dieses Token handeln darf. Ein Token für mehr als einen Arbeitsbereich nennt den Arbeitsbereich bei jedem Aufruf im Header x-cat-factory-workspace.",
864
+ "reachThis": "Nur dieser Arbeitsbereich",
865
+ "reachAll": "Alle Arbeitsbereiche des Kontos",
866
+ "reachSelected": "Ausgewählte Arbeitsbereiche",
867
+ "reachPick": "Arbeitsbereiche auswählen"
860
868
  },
861
869
  "scopes": {
862
870
  "read": "Nur Lesen",
@@ -2603,7 +2611,16 @@
2603
2611
  },
2604
2612
  "failingRun": "{kind} · {at}",
2605
2613
  "failingRunsMore": "{count} weitere nicht angezeigt",
2606
- "failingRunGone": "Dieser Lauf ist nicht mehr geladen und hat keine Aufgabe zum Öffnen."
2614
+ "failingRunGone": "Dieser Lauf ist nicht mehr geladen und hat keine Aufgabe zum Öffnen.",
2615
+ "goTo": {
2616
+ "review": "Prüfen",
2617
+ "answer": "Antworten",
2618
+ "test": "Testen",
2619
+ "confirm": "Bestätigen",
2620
+ "triage": "Sichten",
2621
+ "choose": "Auswählen",
2622
+ "open": "Öffnen"
2623
+ }
2607
2624
  },
2608
2625
  "aiProvidersBanner": {
2609
2626
  "setup": {
@@ -6425,7 +6442,8 @@
6425
6442
  "delegated_claim_missing": "Für diesen Schritt gibt es keine externe Arbeit zu prüfen",
6426
6443
  "thread_busy": "Dieser Thread antwortet noch",
6427
6444
  "draft_conflict": "Dieser Entwurf hat sich geändert",
6428
- "session_stale": "Der Pull Request hat sich weiterentwickelt"
6445
+ "session_stale": "Der Pull Request hat sich weiterentwickelt",
6446
+ "cursor_expired": "Der Änderungsfeed ist über diesen Cursor hinaus"
6429
6447
  },
6430
6448
  "description": {
6431
6449
  "dependencies_unmet": "Diese Aufgabe hängt von anderen ab, die noch nicht abgeschlossen sind. Schließe sie ab oder gib sie frei und starte dann erneut.",
@@ -6485,7 +6503,8 @@
6485
6503
  "delegated_claim_missing": "Der Eintrag des Laufs darüber, was an den externen Executor geschickt wurde, fehlt, also gibt es nichts abzufragen. Am Executor selbst ist nichts falsch. Lade den Lauf neu und starte den Schritt erneut, falls er weiterhin hängt.",
6486
6504
  "thread_busy": "Die vorherige Frage in diesem Thread ist noch nicht beantwortet. Warte auf die Antwort oder frag in einem anderen Thread.",
6487
6505
  "draft_conflict": "Dieser Kommentarentwurf wurde seit dem Laden bearbeitet, gepostet oder verworfen. Lade die Entwürfe neu und nimm deine Änderung erneut vor.",
6488
- "session_stale": "Nach der Vorbereitung dieser Review kamen neue Commits hinzu, daher zeigen die Entwürfe möglicherweise auf falsche Zeilen. Aktualisiere die Review, prüfe die Entwürfe und poste dann."
6506
+ "session_stale": "Nach der Vorbereitung dieser Review kamen neue Commits hinzu, daher zeigen die Entwürfe möglicherweise auf falsche Zeilen. Aktualisiere die Review, prüfe die Entwürfe und poste dann.",
6507
+ "cursor_expired": "Die Änderungen nach diesem Cursor werden nicht mehr aufbewahrt. Erstelle einen neuen Snapshot des Verzeichnisses und folge dem Feed ab der Position, die er meldet."
6489
6508
  },
6490
6509
  "action": {
6491
6510
  "connectGitHub": "GitHub verbinden",
@@ -810,7 +810,8 @@
810
810
  "delegated_claim_missing": "This step has no external work to check",
811
811
  "thread_busy": "This thread is still answering",
812
812
  "draft_conflict": "This draft changed",
813
- "session_stale": "The pull request has moved on"
813
+ "session_stale": "The pull request has moved on",
814
+ "cursor_expired": "The change feed moved past this cursor"
814
815
  },
815
816
  "description": {
816
817
  "dependencies_unmet": "This task depends on others that aren't finished yet. Complete or unblock them, then start it again.",
@@ -873,7 +874,8 @@
873
874
  "delegated_claim_missing": "The run’s record of what it sent to the external executor is missing, so there is nothing to poll. Nothing is wrong with the executor itself. Reload the run, and re-run the step if it is still stuck.",
874
875
  "thread_busy": "The previous question in this thread has not been answered yet. Wait for that answer, or ask in another thread.",
875
876
  "draft_conflict": "Someone edited, posted or discarded this comment draft since you loaded it. Reload the drafts and make your change again.",
876
- "session_stale": "New commits landed after this review was prepared, so its drafts may point at the wrong lines. Refresh the review, check the drafts, then post."
877
+ "session_stale": "New commits landed after this review was prepared, so its drafts may point at the wrong lines. Refresh the review, check the drafts, then post.",
878
+ "cursor_expired": "The changes after this cursor are no longer kept. Take a fresh snapshot of the directory, then follow the feed from the position it reports."
877
879
  },
878
880
  "action": {
879
881
  "connectGitHub": "Connect GitHub",
@@ -2613,7 +2615,16 @@
2613
2615
  },
2614
2616
  "failingRun": "{kind} · {at}",
2615
2617
  "failingRunsMore": "{count} more not shown",
2616
- "failingRunGone": "This run is no longer loaded and has no task to open."
2618
+ "failingRunGone": "This run is no longer loaded and has no task to open.",
2619
+ "goTo": {
2620
+ "review": "Review",
2621
+ "answer": "Answer",
2622
+ "test": "Test",
2623
+ "confirm": "Confirm",
2624
+ "triage": "Triage",
2625
+ "choose": "Choose",
2626
+ "open": "Open"
2627
+ }
2617
2628
  },
2618
2629
  "aiProvidersBanner": {
2619
2630
  "setup": {
@@ -3899,7 +3910,9 @@
3899
3910
  "createdByKey": "API key {id}",
3900
3911
  "revoke": "Revoke token",
3901
3912
  "boundToYou": "Your subscription",
3902
- "boundToOther": "{user}'s subscription"
3913
+ "boundToOther": "{user}'s subscription",
3914
+ "reachAll": "All workspaces",
3915
+ "reachCount": "{count} workspaces"
3903
3916
  },
3904
3917
  "add": {
3905
3918
  "heading": "Create a token",
@@ -3913,7 +3926,13 @@
3913
3926
  "identitySystem": "This workspace (system token)",
3914
3927
  "identitySelf": "Me (personal token)",
3915
3928
  "identitySystemHelp": "Runs started with this token belong to the workspace and are attributed to no person. It cannot use anyone’s personal Claude / Codex / GLM subscription, so a task pinned to one of those models is refused rather than charged to someone who is not there. Use this for CI and shared integrations.",
3916
- "identitySelfHelp": "Runs started with this token count as yours and can use your personal Claude / Codex / GLM subscription. Every such call must also send your personal password in the X-Personal-Password header; it is never stored. Use this to drive your own headless runs."
3929
+ "identitySelfHelp": "Runs started with this token count as yours and can use your personal Claude / Codex / GLM subscription. Every such call must also send your personal password in the X-Personal-Password header; it is never stored. Use this to drive your own headless runs.",
3930
+ "reach": "Workspaces",
3931
+ "reachHelp": "Which workspaces this token can act on. A token reaching more than one names the workspace on each call in the x-cat-factory-workspace header.",
3932
+ "reachThis": "This workspace only",
3933
+ "reachAll": "Every workspace in the account",
3934
+ "reachSelected": "Selected workspaces",
3935
+ "reachPick": "Choose workspaces"
3917
3936
  },
3918
3937
  "scopes": {
3919
3938
  "read": "Read only",
@@ -732,7 +732,8 @@
732
732
  "delegated_claim_missing": "Este paso no tiene trabajo externo que consultar",
733
733
  "thread_busy": "Este hilo todavía está respondiendo",
734
734
  "draft_conflict": "Este borrador ha cambiado",
735
- "session_stale": "La pull request ha avanzado"
735
+ "session_stale": "La pull request ha avanzado",
736
+ "cursor_expired": "El registro de cambios ya superó este cursor"
736
737
  },
737
738
  "description": {
738
739
  "dependencies_unmet": "Esta tarea depende de otras que aún no están terminadas. Complétalas o desbloquéalas y vuelve a iniciarla.",
@@ -792,7 +793,8 @@
792
793
  "delegated_claim_missing": "Falta el registro de lo que la ejecución envió al ejecutor externo, así que no hay nada que consultar. El ejecutor en sí no tiene ningún problema. Recarga la ejecución y vuelve a lanzar el paso si sigue atascado.",
793
794
  "thread_busy": "La pregunta anterior de este hilo aún no tiene respuesta. Espera esa respuesta o pregunta en otro hilo.",
794
795
  "draft_conflict": "Este borrador de comentario se ha editado, publicado o descartado desde que lo cargaste. Vuelve a cargar los borradores y repite tu cambio.",
795
- "session_stale": "Llegaron commits nuevos después de preparar esta revisión, así que sus borradores pueden apuntar a líneas equivocadas. Actualiza la revisión, revisa los borradores y luego publica."
796
+ "session_stale": "Llegaron commits nuevos después de preparar esta revisión, así que sus borradores pueden apuntar a líneas equivocadas. Actualiza la revisión, revisa los borradores y luego publica.",
797
+ "cursor_expired": "Los cambios posteriores a este cursor ya no se conservan. Toma una instantánea nueva del directorio y sigue el registro desde la posición que indique."
796
798
  },
797
799
  "action": {
798
800
  "connectGitHub": "Conectar GitHub",
@@ -2476,7 +2478,16 @@
2476
2478
  },
2477
2479
  "failingRun": "{kind} · {at}",
2478
2480
  "failingRunsMore": "{count} más sin mostrar",
2479
- "failingRunGone": "Esta ejecución ya no está cargada y no tiene ninguna tarea que abrir."
2481
+ "failingRunGone": "Esta ejecución ya no está cargada y no tiene ninguna tarea que abrir.",
2482
+ "goTo": {
2483
+ "review": "Revisar",
2484
+ "answer": "Responder",
2485
+ "test": "Probar",
2486
+ "confirm": "Confirmar",
2487
+ "triage": "Clasificar",
2488
+ "choose": "Elegir",
2489
+ "open": "Abrir"
2490
+ }
2480
2491
  },
2481
2492
  "aiProvidersBanner": {
2482
2493
  "setup": {
@@ -3591,7 +3602,9 @@
3591
3602
  "createdByKey": "la clave de API {id}",
3592
3603
  "revoke": "Revocar token",
3593
3604
  "boundToYou": "Tu suscripción",
3594
- "boundToOther": "Suscripción de {user}"
3605
+ "boundToOther": "Suscripción de {user}",
3606
+ "reachAll": "Todos los espacios de trabajo",
3607
+ "reachCount": "{count} espacios de trabajo"
3595
3608
  },
3596
3609
  "add": {
3597
3610
  "heading": "Crear un token",
@@ -3605,7 +3618,13 @@
3605
3618
  "identitySystem": "Este espacio de trabajo (token de sistema)",
3606
3619
  "identitySelf": "Yo (token personal)",
3607
3620
  "identitySystemHelp": "Las ejecuciones iniciadas con este token pertenecen al espacio de trabajo y no se atribuyen a ninguna persona. No puede usar la suscripción personal de Claude / Codex / GLM de nadie, así que una tarea fijada a uno de esos modelos se rechaza en lugar de cargarse a alguien ausente. Úsalo para CI e integraciones compartidas.",
3608
- "identitySelfHelp": "Las ejecuciones iniciadas con este token se te atribuyen y pueden usar tu suscripción personal de Claude / Codex / GLM. Cada llamada debe enviar además tu contraseña personal en la cabecera X-Personal-Password; nunca se almacena. Úsalo para tus propias ejecuciones headless."
3621
+ "identitySelfHelp": "Las ejecuciones iniciadas con este token se te atribuyen y pueden usar tu suscripción personal de Claude / Codex / GLM. Cada llamada debe enviar además tu contraseña personal en la cabecera X-Personal-Password; nunca se almacena. Úsalo para tus propias ejecuciones headless.",
3622
+ "reach": "Espacios de trabajo",
3623
+ "reachHelp": "En qué espacios de trabajo puede actuar este token. Un token con acceso a más de uno indica el espacio de trabajo en cada llamada con la cabecera x-cat-factory-workspace.",
3624
+ "reachThis": "Solo este espacio de trabajo",
3625
+ "reachAll": "Todos los espacios de trabajo de la cuenta",
3626
+ "reachSelected": "Espacios de trabajo seleccionados",
3627
+ "reachPick": "Elige espacios de trabajo"
3609
3628
  },
3610
3629
  "scopes": {
3611
3630
  "read": "Solo lectura",
@@ -732,7 +732,8 @@
732
732
  "delegated_claim_missing": "Cette étape n’a aucun travail externe à interroger",
733
733
  "thread_busy": "Ce fil répond encore",
734
734
  "draft_conflict": "Ce brouillon a changé",
735
- "session_stale": "La pull request a évolué"
735
+ "session_stale": "La pull request a évolué",
736
+ "cursor_expired": "Le flux de modifications a dépassé ce curseur"
736
737
  },
737
738
  "description": {
738
739
  "dependencies_unmet": "Cette tâche dépend d'autres qui ne sont pas encore terminées. Terminez-les ou débloquez-les, puis relancez-la.",
@@ -792,7 +793,8 @@
792
793
  "delegated_claim_missing": "L’enregistrement de ce que l’exécution a envoyé à l’exécuteur externe est absent : il n’y a donc rien à interroger. L’exécuteur lui-même n’a aucun problème. Rechargez l’exécution et relancez l’étape si elle reste bloquée.",
793
794
  "thread_busy": "La question précédente de ce fil n'a pas encore reçu de réponse. Attends cette réponse ou pose ta question dans un autre fil.",
794
795
  "draft_conflict": "Ce brouillon de commentaire a été modifié, publié ou abandonné depuis que tu l'as chargé. Recharge les brouillons et refais ta modification.",
795
- "session_stale": "De nouveaux commits sont arrivés après la préparation de cette revue, donc ses brouillons peuvent viser les mauvaises lignes. Actualise la revue, vérifie les brouillons, puis publie."
796
+ "session_stale": "De nouveaux commits sont arrivés après la préparation de cette revue, donc ses brouillons peuvent viser les mauvaises lignes. Actualise la revue, vérifie les brouillons, puis publie.",
797
+ "cursor_expired": "Les modifications après ce curseur ne sont plus conservées. Prenez un nouvel instantané de l’annuaire, puis suivez le flux à partir de la position qu’il indique."
796
798
  },
797
799
  "action": {
798
800
  "connectGitHub": "Connecter GitHub",
@@ -2476,7 +2478,16 @@
2476
2478
  },
2477
2479
  "failingRun": "{kind} · {at}",
2478
2480
  "failingRunsMore": "{count} de plus non affichées",
2479
- "failingRunGone": "Cette exécution n'est plus chargée et n'a aucune tâche à ouvrir."
2481
+ "failingRunGone": "Cette exécution n'est plus chargée et n'a aucune tâche à ouvrir.",
2482
+ "goTo": {
2483
+ "review": "Relire",
2484
+ "answer": "Répondre",
2485
+ "test": "Tester",
2486
+ "confirm": "Confirmer",
2487
+ "triage": "Trier",
2488
+ "choose": "Choisir",
2489
+ "open": "Ouvrir"
2490
+ }
2480
2491
  },
2481
2492
  "aiProvidersBanner": {
2482
2493
  "setup": {
@@ -3591,7 +3602,9 @@
3591
3602
  "createdByKey": "la clé d’API {id}",
3592
3603
  "revoke": "Révoquer le jeton",
3593
3604
  "boundToYou": "Votre abonnement",
3594
- "boundToOther": "Abonnement de {user}"
3605
+ "boundToOther": "Abonnement de {user}",
3606
+ "reachAll": "Tous les espaces de travail",
3607
+ "reachCount": "{count} espaces de travail"
3595
3608
  },
3596
3609
  "add": {
3597
3610
  "heading": "Créer un jeton",
@@ -3605,7 +3618,13 @@
3605
3618
  "identitySystem": "Cet espace de travail (jeton système)",
3606
3619
  "identitySelf": "Moi (jeton personnel)",
3607
3620
  "identitySystemHelp": "Les exécutions lancées avec ce jeton appartiennent à l'espace de travail et ne sont attribuées à personne. Il ne peut utiliser l'abonnement personnel Claude / Codex / GLM de qui que ce soit : une tâche fixée sur un tel modèle est refusée plutôt que facturée à quelqu'un d'absent. À utiliser pour la CI et les intégrations partagées.",
3608
- "identitySelfHelp": "Les exécutions lancées avec ce jeton vous sont attribuées et peuvent utiliser votre abonnement personnel Claude / Codex / GLM. Chaque appel doit aussi envoyer votre mot de passe personnel dans l'en-tête X-Personal-Password ; il n'est jamais conservé. À utiliser pour vos propres exécutions headless."
3621
+ "identitySelfHelp": "Les exécutions lancées avec ce jeton vous sont attribuées et peuvent utiliser votre abonnement personnel Claude / Codex / GLM. Chaque appel doit aussi envoyer votre mot de passe personnel dans l'en-tête X-Personal-Password ; il n'est jamais conservé. À utiliser pour vos propres exécutions headless.",
3622
+ "reach": "Espaces de travail",
3623
+ "reachHelp": "Les espaces de travail sur lesquels ce jeton peut agir. Un jeton qui en couvre plusieurs indique l’espace de travail à chaque appel dans l’en-tête x-cat-factory-workspace.",
3624
+ "reachThis": "Cet espace de travail uniquement",
3625
+ "reachAll": "Tous les espaces de travail du compte",
3626
+ "reachSelected": "Espaces de travail sélectionnés",
3627
+ "reachPick": "Choisir des espaces de travail"
3609
3628
  },
3610
3629
  "scopes": {
3611
3630
  "read": "Lecture seule",
@@ -732,7 +732,8 @@
732
732
  "delegated_claim_missing": "לשלב זה אין עבודה חיצונית לבדיקה",
733
733
  "thread_busy": "השרשור הזה עדיין עונה",
734
734
  "draft_conflict": "הטיוטה הזו השתנתה",
735
- "session_stale": "בקשת המיזוג התקדמה"
735
+ "session_stale": "בקשת המיזוג התקדמה",
736
+ "cursor_expired": "פיד השינויים התקדם מעבר לסמן הזה"
736
737
  },
737
738
  "description": {
738
739
  "dependencies_unmet": "משימה זו תלויה במשימות אחרות שטרם הושלמו. השלם או שחרר אותן, ולאחר מכן הפעל אותה שוב.",
@@ -792,7 +793,8 @@
792
793
  "delegated_claim_missing": "הרישום של מה שההרצה שלחה למבצע החיצוני חסר, ולכן אין מה לבדוק. אין שום בעיה במבצע עצמו. טענו מחדש את ההרצה, והריצו את השלב שוב אם הוא עדיין תקוע.",
793
794
  "thread_busy": "השאלה הקודמת בשרשור הזה עדיין לא נענתה. המתינו לתשובה או שאלו בשרשור אחר.",
794
795
  "draft_conflict": "טיוטת התגובה נערכה, פורסמה או נמחקה מאז שנטענה. טענו מחדש את הטיוטות ובצעו את השינוי שוב.",
795
- "session_stale": "נוספו קומיטים חדשים אחרי שהסקירה הוכנה, ולכן הטיוטות עלולות להצביע על שורות שגויות. רעננו את הסקירה, בדקו את הטיוטות ואז פרסמו."
796
+ "session_stale": "נוספו קומיטים חדשים אחרי שהסקירה הוכנה, ולכן הטיוטות עלולות להצביע על שורות שגויות. רעננו את הסקירה, בדקו את הטיוטות ואז פרסמו.",
797
+ "cursor_expired": "השינויים שאחרי הסמן הזה כבר לא נשמרים. צרו תמונת מצב חדשה של הספרייה ואז המשיכו לעקוב אחרי הפיד מהמיקום שהיא מדווחת."
796
798
  },
797
799
  "action": {
798
800
  "connectGitHub": "חבר את GitHub",
@@ -2476,7 +2478,16 @@
2476
2478
  },
2477
2479
  "failingRun": "{kind} · {at}",
2478
2480
  "failingRunsMore": "עוד {count} שאינן מוצגות",
2479
- "failingRunGone": "ההרצה הזו כבר לא טעונה ואין לה משימה לפתוח."
2481
+ "failingRunGone": "ההרצה הזו כבר לא טעונה ואין לה משימה לפתוח.",
2482
+ "goTo": {
2483
+ "review": "סקירה",
2484
+ "answer": "מענה",
2485
+ "test": "בדיקה",
2486
+ "confirm": "אישור",
2487
+ "triage": "מיון",
2488
+ "choose": "בחירה",
2489
+ "open": "פתיחה"
2490
+ }
2480
2491
  },
2481
2492
  "aiProvidersBanner": {
2482
2493
  "setup": {
@@ -3732,7 +3743,9 @@
3732
3743
  "createdByKey": "מפתח API {id}",
3733
3744
  "revoke": "בטל אסימון",
3734
3745
  "boundToYou": "המנוי שלך",
3735
- "boundToOther": "המנוי של {user}"
3746
+ "boundToOther": "המנוי של {user}",
3747
+ "reachAll": "כל סביבות העבודה",
3748
+ "reachCount": "{count} סביבות עבודה"
3736
3749
  },
3737
3750
  "add": {
3738
3751
  "heading": "צור אסימון",
@@ -3746,7 +3759,13 @@
3746
3759
  "identitySystem": "סביבת העבודה הזו (אסימון מערכת)",
3747
3760
  "identitySelf": "אני (אסימון אישי)",
3748
3761
  "identitySystemHelp": "הרצות שמתחילות עם האסימון הזה שייכות לסביבת העבודה ואינן משויכות לאף אדם. הוא אינו יכול להשתמש במנוי האישי של אף אחד ל-Claude / Codex / GLM, ולכן משימה שמוצמדת למודל כזה תידחה במקום להיזקף לחובת מי שאינו נוכח. השתמשו בו ל-CI ולאינטגרציות משותפות.",
3749
- "identitySelfHelp": "הרצות שמתחילות עם האסימון הזה נחשבות שלך ויכולות להשתמש במנוי האישי שלך ל-Claude / Codex / GLM. כל קריאה כזו חייבת לשלוח גם את הסיסמה האישית שלך בכותרת X-Personal-Password; היא לעולם אינה נשמרת. השתמשו בו להרצות headless משלכם."
3762
+ "identitySelfHelp": "הרצות שמתחילות עם האסימון הזה נחשבות שלך ויכולות להשתמש במנוי האישי שלך ל-Claude / Codex / GLM. כל קריאה כזו חייבת לשלוח גם את הסיסמה האישית שלך בכותרת X-Personal-Password; היא לעולם אינה נשמרת. השתמשו בו להרצות headless משלכם.",
3763
+ "reach": "סביבות עבודה",
3764
+ "reachHelp": "באילו סביבות עבודה האסימון הזה יכול לפעול. אסימון שמגיע ליותר מסביבה אחת מציין את סביבת העבודה בכל קריאה בכותרת x-cat-factory-workspace.",
3765
+ "reachThis": "סביבת העבודה הזו בלבד",
3766
+ "reachAll": "כל סביבות העבודה בחשבון",
3767
+ "reachSelected": "סביבות עבודה נבחרות",
3768
+ "reachPick": "בחירת סביבות עבודה"
3750
3769
  },
3751
3770
  "scopes": {
3752
3771
  "read": "קריאה בלבד",
@@ -842,7 +842,9 @@
842
842
  "createdByKey": "la chiave API {id}",
843
843
  "revoke": "Revoca token",
844
844
  "boundToYou": "Il tuo abbonamento",
845
- "boundToOther": "Abbonamento di {user}"
845
+ "boundToOther": "Abbonamento di {user}",
846
+ "reachAll": "Tutti gli spazi di lavoro",
847
+ "reachCount": "{count} spazi di lavoro"
846
848
  },
847
849
  "add": {
848
850
  "heading": "Crea un token",
@@ -856,7 +858,13 @@
856
858
  "identitySystem": "Questo spazio di lavoro (token di sistema)",
857
859
  "identitySelf": "Io (token personale)",
858
860
  "identitySystemHelp": "Le esecuzioni avviate con questo token appartengono allo spazio di lavoro e non sono attribuite a nessuna persona. Non può usare l'abbonamento personale Claude / Codex / GLM di nessuno: un'attività fissata su uno di quei modelli viene rifiutata anziché addebitata a chi non c'è. Usalo per la CI e le integrazioni condivise.",
859
- "identitySelfHelp": "Le esecuzioni avviate con questo token sono attribuite a te e possono usare il tuo abbonamento personale Claude / Codex / GLM. Ogni chiamata deve inviare anche la tua password personale nell'intestazione X-Personal-Password; non viene mai memorizzata. Usalo per le tue esecuzioni headless."
861
+ "identitySelfHelp": "Le esecuzioni avviate con questo token sono attribuite a te e possono usare il tuo abbonamento personale Claude / Codex / GLM. Ogni chiamata deve inviare anche la tua password personale nell'intestazione X-Personal-Password; non viene mai memorizzata. Usalo per le tue esecuzioni headless.",
862
+ "reach": "Spazi di lavoro",
863
+ "reachHelp": "Su quali spazi di lavoro questo token può agire. Un token che ne raggiunge più di uno indica lo spazio di lavoro a ogni chiamata nell’header x-cat-factory-workspace.",
864
+ "reachThis": "Solo questo spazio di lavoro",
865
+ "reachAll": "Tutti gli spazi di lavoro dell’account",
866
+ "reachSelected": "Spazi di lavoro selezionati",
867
+ "reachPick": "Scegli gli spazi di lavoro"
860
868
  },
861
869
  "scopes": {
862
870
  "read": "Sola lettura",
@@ -2603,7 +2611,16 @@
2603
2611
  },
2604
2612
  "failingRun": "{kind} · {at}",
2605
2613
  "failingRunsMore": "altre {count} non mostrate",
2606
- "failingRunGone": "Questa esecuzione non è più caricata e non ha alcuna attività da aprire."
2614
+ "failingRunGone": "Questa esecuzione non è più caricata e non ha alcuna attività da aprire.",
2615
+ "goTo": {
2616
+ "review": "Rivedi",
2617
+ "answer": "Rispondi",
2618
+ "test": "Testa",
2619
+ "confirm": "Conferma",
2620
+ "triage": "Smista",
2621
+ "choose": "Scegli",
2622
+ "open": "Apri"
2623
+ }
2607
2624
  },
2608
2625
  "aiProvidersBanner": {
2609
2626
  "setup": {
@@ -6425,7 +6442,8 @@
6425
6442
  "delegated_claim_missing": "Questo passo non ha lavoro esterno da controllare",
6426
6443
  "thread_busy": "Questa discussione sta ancora rispondendo",
6427
6444
  "draft_conflict": "Questa bozza è cambiata",
6428
- "session_stale": "La pull request è andata avanti"
6445
+ "session_stale": "La pull request è andata avanti",
6446
+ "cursor_expired": "Il feed delle modifiche ha superato questo cursore"
6429
6447
  },
6430
6448
  "description": {
6431
6449
  "dependencies_unmet": "Questa attività dipende da altre non ancora completate. Completale o sbloccale, poi avviala di nuovo.",
@@ -6485,7 +6503,8 @@
6485
6503
  "delegated_claim_missing": "Manca la registrazione di ciò che l’esecuzione ha inviato all’esecutore esterno, quindi non c’è nulla da interrogare. L’esecutore in sé non ha alcun problema. Ricarica l’esecuzione e rilancia il passo se resta bloccato.",
6486
6504
  "thread_busy": "La domanda precedente in questa discussione non ha ancora una risposta. Attendi la risposta o chiedi in un'altra discussione.",
6487
6505
  "draft_conflict": "Questa bozza di commento è stata modificata, pubblicata o scartata da quando l'hai caricata. Ricarica le bozze e ripeti la modifica.",
6488
- "session_stale": "Sono arrivati nuovi commit dopo la preparazione di questa revisione, quindi le bozze potrebbero puntare alle righe sbagliate. Aggiorna la revisione, controlla le bozze e poi pubblica."
6506
+ "session_stale": "Sono arrivati nuovi commit dopo la preparazione di questa revisione, quindi le bozze potrebbero puntare alle righe sbagliate. Aggiorna la revisione, controlla le bozze e poi pubblica.",
6507
+ "cursor_expired": "Le modifiche successive a questo cursore non sono più conservate. Acquisisci una nuova istantanea della directory, poi segui il feed dalla posizione che riporta."
6489
6508
  },
6490
6509
  "action": {
6491
6510
  "connectGitHub": "Collega GitHub",
@@ -732,7 +732,8 @@
732
732
  "delegated_claim_missing": "このステップには確認できる外部の処理がありません",
733
733
  "thread_busy": "このスレッドはまだ回答中です",
734
734
  "draft_conflict": "この下書きは変更されています",
735
- "session_stale": "プルリクエストが更新されています"
735
+ "session_stale": "プルリクエストが更新されています",
736
+ "cursor_expired": "変更フィードがこのカーソルを過ぎました"
736
737
  },
737
738
  "description": {
738
739
  "dependencies_unmet": "このタスクは、まだ完了していない他のタスクに依存しています。それらを完了または解除してから、もう一度開始してください。",
@@ -792,7 +793,8 @@
792
793
  "delegated_claim_missing": "外部エグゼキューターに何を送ったかという実行の記録が失われているため、ポーリングする対象がありません。エグゼキューター自体に問題はありません。実行を再読み込みし、まだ止まったままならステップを再実行してください。",
793
794
  "thread_busy": "このスレッドの前の質問にまだ回答がありません。回答を待つか、別のスレッドで質問してください。",
794
795
  "draft_conflict": "読み込んだ後に、このコメントの下書きが編集、投稿、または破棄されました。下書きを再読み込みして、もう一度変更してください。",
795
- "session_stale": "このレビューの準備後に新しいコミットが追加されたため、下書きが誤った行を指している可能性があります。レビューを更新し、下書きを確認してから投稿してください。"
796
+ "session_stale": "このレビューの準備後に新しいコミットが追加されたため、下書きが誤った行を指している可能性があります。レビューを更新し、下書きを確認してから投稿してください。",
797
+ "cursor_expired": "このカーソル以降の変更はもう保持されていません。ディレクトリの新しいスナップショットを取得し、その位置からフィードを追ってください。"
796
798
  },
797
799
  "action": {
798
800
  "connectGitHub": "GitHub に接続",
@@ -2476,7 +2478,16 @@
2476
2478
  },
2477
2479
  "failingRun": "{kind} · {at}",
2478
2480
  "failingRunsMore": "他に {count} 件(非表示)",
2479
- "failingRunGone": "この実行はすでに読み込まれておらず、開けるタスクもありません。"
2481
+ "failingRunGone": "この実行はすでに読み込まれておらず、開けるタスクもありません。",
2482
+ "goTo": {
2483
+ "review": "レビュー",
2484
+ "answer": "回答",
2485
+ "test": "テスト",
2486
+ "confirm": "確認",
2487
+ "triage": "仕分け",
2488
+ "choose": "選択",
2489
+ "open": "開く"
2490
+ }
2480
2491
  },
2481
2492
  "aiProvidersBanner": {
2482
2493
  "setup": {
@@ -3732,7 +3743,9 @@
3732
3743
  "createdByKey": "APIキー {id}",
3733
3744
  "revoke": "トークンを取り消す",
3734
3745
  "boundToYou": "あなたのサブスクリプション",
3735
- "boundToOther": "{user} のサブスクリプション"
3746
+ "boundToOther": "{user} のサブスクリプション",
3747
+ "reachAll": "すべてのワークスペース",
3748
+ "reachCount": "{count} 個のワークスペース"
3736
3749
  },
3737
3750
  "add": {
3738
3751
  "heading": "トークンを作成",
@@ -3746,7 +3759,13 @@
3746
3759
  "identitySystem": "このワークスペース(システムトークン)",
3747
3760
  "identitySelf": "自分(個人トークン)",
3748
3761
  "identitySystemHelp": "このトークンで開始した実行はワークスペースに属し、個人には紐づきません。誰の個人 Claude / Codex / GLM サブスクリプションも利用できないため、それらのモデルを指定したタスクは、不在の相手に課金される代わりに拒否されます。CI や共有連携にはこちらを使ってください。",
3749
- "identitySelfHelp": "このトークンで開始した実行はあなたのものとして扱われ、あなたの個人 Claude / Codex / GLM サブスクリプションを利用できます。その際は毎回のリクエストで X-Personal-Password ヘッダーに個人パスワードを送る必要があります。パスワードは保存されません。自分のヘッドレス実行にはこちらを使ってください。"
3762
+ "identitySelfHelp": "このトークンで開始した実行はあなたのものとして扱われ、あなたの個人 Claude / Codex / GLM サブスクリプションを利用できます。その際は毎回のリクエストで X-Personal-Password ヘッダーに個人パスワードを送る必要があります。パスワードは保存されません。自分のヘッドレス実行にはこちらを使ってください。",
3763
+ "reach": "ワークスペース",
3764
+ "reachHelp": "このトークンが操作できるワークスペース。複数のワークスペースに届くトークンは、各呼び出しで x-cat-factory-workspace ヘッダーにワークスペースを指定します。",
3765
+ "reachThis": "このワークスペースのみ",
3766
+ "reachAll": "アカウント内のすべてのワークスペース",
3767
+ "reachSelected": "選択したワークスペース",
3768
+ "reachPick": "ワークスペースを選択"
3750
3769
  },
3751
3770
  "scopes": {
3752
3771
  "read": "読み取り専用",