@cat-factory/app 0.173.0 → 0.176.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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The confirm-gated "discard this run" action, shared by every surface that offers it so they
3
+ * can't drift on what it does or how loudly it asks first.
4
+ *
5
+ * Destructive and distinct from a STOP: stop halts a run but keeps it readable and retryable,
6
+ * while this deletes it outright and returns the block to `planned`. That is what makes it the
7
+ * escape hatch for a WEDGED run — a run whose driver will never settle can't be waited out, and
8
+ * until the block's `executionId` clears nothing else may start on it.
9
+ *
10
+ * Two callers today: the inspector's execution panel (tasks and initiatives alike) and the
11
+ * initiative planning window, which needs it in-place because a human whose interview stalled is
12
+ * looking at that window, not at the inspector behind it.
13
+ */
14
+ export function useRunReset() {
15
+ const execution = useExecutionStore()
16
+ const access = useWorkspaceAccess()
17
+ const { confirm } = useConfirm()
18
+ const { t } = useI18n()
19
+
20
+ /** True while a discard is in flight (drives the button spinner). */
21
+ const resetting = ref(false)
22
+
23
+ /**
24
+ * Confirm, then discard the block's run. Resolves `true` only when the run was actually
25
+ * discarded, so a caller can act on it (the planning window closes itself). A read-only viewer
26
+ * no-ops — every button binding this is disabled for them, and this guards the rest.
27
+ */
28
+ async function resetRun(blockId: string): Promise<boolean> {
29
+ if (resetting.value || !access.canExecuteRuns.value) return false
30
+ const ok = await confirm({
31
+ title: t('inspector.execution.resetConfirm.title'),
32
+ description: t('inspector.execution.resetConfirm.body'),
33
+ variant: 'destructive',
34
+ confirmLabel: t('inspector.execution.resetConfirm.confirm'),
35
+ icon: 'i-lucide-trash-2',
36
+ })
37
+ if (!ok) return false
38
+ resetting.value = true
39
+ try {
40
+ await execution.cancel(blockId)
41
+ return true
42
+ } finally {
43
+ resetting.value = false
44
+ }
45
+ }
46
+
47
+ return { resetting, resetRun }
48
+ }
@@ -99,36 +99,71 @@ describe('navSlotFilter', () => {
99
99
  expect(kept).toContain('integrations-hub')
100
100
  expect(kept).toContain('workspace-settings')
101
101
  expect(kept).toContain('model-config')
102
- // ...and so does every destination that is the SOLE route to its capability, however deep
103
- // it feels: authoring a flow, the standards library, the PREnv/runner plumbing, and the
104
- // aggregate run-health / spend views.
102
+ // ...and so does everything the everyday delivery loop runs on, however deep it feels:
103
+ // authoring a flow, the standards library, and the PREnv/runner plumbing.
105
104
  expect(kept).toContain('build-pipeline')
106
105
  expect(kept).toContain('fragments')
107
106
  expect(kept).toContain('infrastructure')
108
107
  expect(kept).toContain('environment-setup')
109
- expect(kept).toContain('operator-dashboard')
110
- expect(kept).toContain('reports')
111
- // What drops is beside the delivery path (experimentation) or a shortcut into a surface
112
- // basic mode already reaches another way.
108
+ // What drops is either a shortcut basic mode reaches another way, or a capability
109
+ // deliberately kept out of the tier (experimentation, one-off repo setup, the
110
+ // deployment-wide operator rollups).
113
111
  expect(kept).not.toContain('sandbox')
114
112
  expect(kept).not.toContain('kaizen')
115
113
  expect(kept).not.toContain('merge-thresholds')
114
+ expect(kept).not.toContain('bootstrap-repo')
115
+ expect(kept).not.toContain('operator-dashboard')
116
+ expect(kept).not.toContain('reports')
116
117
  })
117
118
 
118
- it('never makes an advanced item the only route to its capability', () => {
119
- // The tier's governing rule (see the catalog comment): every advanced destination either
120
- // sits beside the delivery path, or is a shortcut into a surface a basic destination also
121
- // opens. Spelled out here as a table so promoting an item to `advanced` forces an explicit
122
- // claim about how basic mode still reaches it, rather than a silent capability loss.
123
- const ALTERNATIVE_ROUTE: Record<string, string> = {
124
- sandbox: 'none needed - experimentation surface, not on the delivery path',
125
- kaizen: 'none needed - self-grading history, not on the delivery path',
126
- 'merge-thresholds': 'workspace-settings -> Merge tab',
127
- 'service-fragment-defaults': 'workspace-settings -> Service best practices tab',
128
- 'local-models': 'integrations-hub -> Local runners',
119
+ it('states, per advanced item, whether basic mode still reaches its capability', () => {
120
+ // Marking an item `advanced` does one of two things, and which one has to be said out
121
+ // loud. `reached-another-way` promises a basic destination opens the same surface, so
122
+ // nothing is lost. `out-of-tier` concedes the opposite: the capability is ABSENT from
123
+ // basic mode and the tier switch is the only way to it. Both are legitimate; a silent
124
+ // one is not. The table is the claim, and it must match the catalog exactly, so adding
125
+ // `advanced: true` fails here until the reason is written down.
126
+ const REASON: Record<string, { kind: 'reached-another-way' | 'out-of-tier'; why: string }> = {
127
+ 'merge-thresholds': {
128
+ kind: 'reached-another-way',
129
+ why: 'workspace-settings -> Merge tab',
130
+ },
131
+ 'service-fragment-defaults': {
132
+ kind: 'reached-another-way',
133
+ why: 'workspace-settings -> Service best practices tab',
134
+ },
135
+ 'local-models': {
136
+ kind: 'reached-another-way',
137
+ why: 'integrations-hub -> Local runners',
138
+ },
139
+ sandbox: {
140
+ kind: 'out-of-tier',
141
+ why: 'experimentation surface, beside the delivery path',
142
+ },
143
+ kaizen: {
144
+ kind: 'out-of-tier',
145
+ why: 'self-grading history, beside the delivery path',
146
+ },
147
+ 'bootstrap-repo': {
148
+ kind: 'out-of-tier',
149
+ why: 'one-off new-repo setup, not part of running work on an existing board',
150
+ },
151
+ 'operator-dashboard': {
152
+ kind: 'out-of-tier',
153
+ why: 'deployment-wide health rollup - an operator job, not a delivery one',
154
+ },
155
+ reports: {
156
+ kind: 'out-of-tier',
157
+ why: 'deployment-wide spend/activity rollup - an operator job, not a delivery one',
158
+ },
129
159
  }
130
160
  const advanced = NAV_CONTRIBUTIONS.filter((i) => i.advanced).map((i) => i.id)
131
- expect(advanced.sort()).toEqual(Object.keys(ALTERNATIVE_ROUTE).sort())
161
+ expect(advanced.sort()).toEqual(Object.keys(REASON).sort())
162
+ // An `out-of-tier` item is a real capability loss, so the switcher back must survive basic
163
+ // mode — pinned by the next case. Assert here only that every claim carries a reason.
164
+ for (const [id, reason] of Object.entries(REASON)) {
165
+ expect(reason.why.length, `${id} has no stated reason`).toBeGreaterThan(0)
166
+ }
132
167
  })
133
168
 
134
169
  it('keeps the tier switch itself reachable in basic mode', () => {
@@ -153,24 +153,35 @@ const S = (...s: NavSurface[]) => s as readonly NavSurface[]
153
153
  * with accounts enabled).
154
154
  * - one icon per destination across shells.
155
155
  *
156
- * `advanced: true` marks the power-user half, hidden in basic interface mode. The line is
157
- * drawn by ROUTE COUNT, not by how advanced a surface feels: hiding the only way to reach a
158
- * capability removes the capability from the tier, while hiding a shortcut to a surface basic
159
- * mode already reaches removes nothing. So an item is advanced only when both of these hold —
160
- * it is not the sole route to its capability, and nothing on the delivery path needs it:
156
+ * `advanced: true` marks the power-user half, hidden in basic interface mode. Basic is the
157
+ * EVERYDAY DELIVERY SURFACE plan work on a board, run it, review and merge it so an item
158
+ * is advanced when it is not part of that loop. That happens two ways, and the difference
159
+ * matters when reading the list (it is the column `nav-contributions.spec.ts` makes you fill
160
+ * in):
161
161
  *
162
- * - `sandbox` / `kaizen`experimentation and self-grading surfaces, beside the delivery
163
- * path rather than on it.
162
+ * REACHED ANOTHER WAYhiding a shortcut removes nothing, because a basic destination opens
163
+ * the same surface:
164
164
  * - `merge-thresholds` / `service-fragment-defaults` — palette shortcuts into Workspace
165
165
  * settings tabs (Merge, Service best practices), which basic mode reaches via
166
166
  * `workspace-settings`.
167
167
  * - `local-models` — a per-user endpoint knob the Integrations hub already offers.
168
168
  *
169
- * Everything else stays in basic BECAUSE it is a sole route: authoring a flow
170
- * (`build-pipeline`), the standards/skills library (`fragments`, whose only other route is a
171
- * button two levels into Workspace settings), the PREnv + runner plumbing (`infrastructure`,
172
- * `environment-setup`), and the operator/cost views that are the only aggregate read of run
173
- * health and spend (`operator-dashboard`, `reports`).
169
+ * OUT OF THE TIER the sole route, hidden deliberately, so the capability itself is absent
170
+ * from basic mode. This is a product decision, not an oversight: each of these answers a
171
+ * question an everyday delivery user does not ask, and carrying it costs the basic nav more
172
+ * than the capability is worth there. Switching to advanced is the way to it.
173
+ * - `sandbox` / `kaizen` — experimentation and self-grading surfaces, beside the delivery
174
+ * path rather than on it.
175
+ * - `bootstrap-repo` — standing a NEW repository up from a reference architecture: a
176
+ * once-per-service setup act, not part of running work on an existing board.
177
+ * - `operator-dashboard` / `reports` — the deployment-wide health and spend rollups. They
178
+ * read across every workspace for whoever operates the deployment, which is a different
179
+ * job from delivering a task on one board.
180
+ *
181
+ * Everything else stays in basic because the delivery loop needs it: authoring a flow
182
+ * (`build-pipeline`), adding a repo (`add-from-repo`), the standards/skills library
183
+ * (`fragments`), the PREnv + runner plumbing (`infrastructure`, `environment-setup`), and the
184
+ * workspace/model configuration a run actually reads (`workspace-settings`, `model-config`).
174
185
  */
175
186
  export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
176
187
  {
@@ -210,6 +221,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
210
221
  labelKey: 'nav.bootstrapRepo',
211
222
  icon: 'i-lucide-git-branch-plus',
212
223
  surfaces: S('sidebar', 'command'),
224
+ advanced: true,
213
225
  gate: (g) => g.canManageIntegrations,
214
226
  action: 'bootstrapRepo',
215
227
  testId: 'nav-bootstrap-repo',
@@ -388,6 +400,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
388
400
  labelKey: 'nav.operatorDashboard',
389
401
  icon: 'i-lucide-gauge',
390
402
  surfaces: S('sidebar'),
403
+ advanced: true,
391
404
  gate: (g) => g.accountsEnabled && g.isAccountAdmin,
392
405
  action: 'operatorDashboard',
393
406
  testId: 'nav-operator-dashboard',
@@ -398,6 +411,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
398
411
  labelKey: 'nav.reports',
399
412
  icon: 'i-lucide-chart-column',
400
413
  surfaces: S('sidebar'),
414
+ advanced: true,
401
415
  gate: (g) => g.accountsEnabled && g.isAccountAdmin,
402
416
  action: 'reports',
403
417
  testId: 'nav-reports',
@@ -94,9 +94,15 @@ describe('inspector panel group', () => {
94
94
  ])
95
95
  })
96
96
 
97
- it('epic and initiative each show their single inspector', () => {
97
+ it('an epic shows only its children panel', () => {
98
98
  expect(visibleIds(block('epic'))).toEqual(['epic-children'])
99
- expect(visibleIds(block('initiative'))).toEqual(['initiative-inspector'])
99
+ })
100
+
101
+ // An initiative's planning pipeline is an ordinary run, so it shares the task body's execution
102
+ // panel — its own inspector leading, the run detail under it. Pinned because the panel is the
103
+ // only surface carrying the Stop / Discard-run controls that unwedge a stalled planning run.
104
+ it('an initiative shows its inspector, then the shared execution panel', () => {
105
+ expect(visibleIds(block('initiative'))).toEqual(['initiative-inspector', 'task-execution'])
100
106
  })
101
107
 
102
108
  it('no subject selected resolves to no panels', () => {
@@ -74,6 +74,14 @@ export interface InspectorPanelSpec {
74
74
 
75
75
  const isTask = (b: Block) => b.level === 'task'
76
76
  const isFrame = (b: Block) => b.level === 'frame'
77
+ /**
78
+ * A block whose inspector carries a pipeline RUN. An initiative's planning pipeline is an
79
+ * ordinary run of ordinary agent steps (interviewer → analyst → planner → committer), so it
80
+ * gets the same execution panel a task does — step list, live phases, step-detail drill-down,
81
+ * and the Stop / Discard-run controls that are the only way to unwedge a stalled planning run.
82
+ * Before this it had no run surface at all, which is why a stuck plan was a dead end.
83
+ */
84
+ const hasRuns = (b: Block) => isTask(b) || b.level === 'initiative'
77
85
  /** frame OR module — the "container" panels. */
78
86
  const isContainer = (b: Block) => b.level === 'frame' || b.level === 'module'
79
87
  /**
@@ -106,7 +114,8 @@ export const INSPECTOR_PANEL_SPECS: readonly InspectorPanelSpec[] = [
106
114
  { id: 'task-context-docs', order: 10, when: isTask },
107
115
  { id: 'task-context-issues', order: 20, when: isTask },
108
116
  { id: 'recurring-schedule', order: 30, when: isTask },
109
- { id: 'task-execution', order: 40, when: isTask },
117
+ // Shared with the initiative body — the panel renders a RUN, and an initiative has one.
118
+ { id: 'task-execution', order: 40, when: hasRuns },
110
119
  { id: 'task-estimate', order: 50, when: isTask },
111
120
  { id: 'task-dependencies', order: 60, when: isTask },
112
121
  { id: 'task-run-settings', order: 70, when: isTask },
@@ -124,5 +133,8 @@ export const INSPECTOR_PANEL_SPECS: readonly InspectorPanelSpec[] = [
124
133
  // test-infra and release-health panels above it.
125
134
  { id: 'service-validation-checks', order: 180, when: isDeployableFrame },
126
135
  { id: 'epic-children', order: 200, when: (b) => b.level === 'epic' },
127
- { id: 'initiative-inspector', order: 210, when: (b) => b.level === 'initiative' },
136
+ // Ordered BEFORE the shared execution panel (40) so an initiative reads the way a task does:
137
+ // its own identity + controls first, the run detail under them. Levels never overlap, so this
138
+ // number only ever competes with the task ids the initiative body doesn't render.
139
+ { id: 'initiative-inspector', order: 35, when: (b) => b.level === 'initiative' },
128
140
  ]
@@ -119,6 +119,10 @@ export const useObservabilityStore = defineStore('observability', () => {
119
119
  if (existing.some((c) => c.id === activity.id)) return
120
120
  const row: LlmCallMetric = {
121
121
  ...activity,
122
+ // The live event carries the phase (the proxy knows it) but no turn ordinal — that is
123
+ // the harness's job-scoped counter, which a proxied call has no equivalent of. Null is
124
+ // what the stored row will say too, so the live row and the loaded one agree.
125
+ turnIndex: null,
122
126
  promptText: '',
123
127
  promptPrefixCount: 0,
124
128
  promptHash: '',
@@ -948,12 +948,12 @@
948
948
  "inspector": {
949
949
  "detectRepoUnresolved": "Das Repository dieses Blocks gehört nicht zu den verbundenen GitHub-Repos und kann daher nicht gelesen werden. Verbinde oder synchronisiere GitHub dafür und versuche es erneut.",
950
950
  "container": {
951
- "title": "Inhalte",
952
- "hint": "Die Module und Aufgaben innerhalb dieses Blocks. Klicke auf einen Eintrag, um ihn auf dem Board auszuwählen.",
953
- "modules": "Module ({count})",
951
+ "modulesTitle": "Module",
952
+ "modulesHint": "Die Module innerhalb dieses Service. Klicke auf ein Modul, um es auf dem Board auszuwählen.",
954
953
  "taskCount": "{count} Aufgabe | {count} Aufgaben",
955
- "allTasks": "Alle Aufgaben ({count})",
956
- "tasks": "Aufgaben ({count})",
954
+ "tasksTitle": "Aufgaben",
955
+ "allTasksTitle": "Alle Aufgaben",
956
+ "tasksHint": "Die Aufgaben innerhalb dieses Blocks. Klicke auf eine Aufgabe, um sie auf dem Board auszuwählen.",
957
957
  "addTask": "Aufgabe hinzufügen",
958
958
  "noTasks": "Noch keine Aufgaben. Füge eine hinzu, um mit der Arbeit zu beginnen.",
959
959
  "servicesHint": "Services sind langlebig und werden nicht abgeschlossen. Die Arbeit findet in ihren Aufgaben und Modulen statt."
@@ -1259,7 +1259,7 @@
1259
1259
  },
1260
1260
  "execution": {
1261
1261
  "title": "Ausführung",
1262
- "hint": "Der Live-Lauf der Pipeline dieser Aufgabe: jeder Agent-Schritt, sein Fortschritt und der resultierende Pull Request.",
1262
+ "hint": "Der Live-Lauf der Pipeline: jeder Agent-Schritt, sein Fortschritt und der resultierende Pull Request.",
1263
1263
  "stage": {
1264
1264
  "incorporating": "Wird eingearbeitet…",
1265
1265
  "reviewing": "Wird erneut geprüft…",
@@ -1284,10 +1284,10 @@
1284
1284
  "stop": "Stoppen",
1285
1285
  "stopTooltip": "Den Lauf stoppen, aber behalten (lesbar und wiederholbar)",
1286
1286
  "reset": "Zurücksetzen",
1287
- "resetTooltip": "Diesen Lauf verwerfen und die Aufgabe auf geplant zurücksetzen",
1287
+ "resetTooltip": "Diesen Lauf verwerfen und den Status auf geplant zurücksetzen",
1288
1288
  "resetConfirm": {
1289
1289
  "title": "Diesen Lauf verwerfen?",
1290
- "body": "Dies löscht den Lauf und setzt die Aufgabe auf geplant zurück. Dies kann nicht rückgängig gemacht werden.",
1290
+ "body": "Dies löscht den Lauf und setzt den Status auf geplant zurück. Dies kann nicht rückgängig gemacht werden.",
1291
1291
  "confirm": "Lauf verwerfen"
1292
1292
  },
1293
1293
  "mergeConfirm": {
@@ -1622,6 +1622,7 @@
1622
1622
  "deleteRecurringPipeline": "Wiederkehrende Pipeline löschen",
1623
1623
  "deleteTask": "Aufgabe löschen",
1624
1624
  "deleteModule": "Modul löschen",
1625
+ "deleteInitiative": "Initiative löschen",
1625
1626
  "deleteService": "Service löschen",
1626
1627
  "confirmDelete": {
1627
1628
  "task": {
@@ -1632,6 +1633,10 @@
1632
1633
  "title": "Dieses Modul löschen?",
1633
1634
  "body": "\"{name}\" und alles darin wird entfernt. Dies kann nicht rückgängig gemacht werden."
1634
1635
  },
1636
+ "initiative": {
1637
+ "title": "Diese Initiative löschen?",
1638
+ "body": "\"{name}\" und ihr Plan werden entfernt. Bereits erstellte Aufgaben bleiben auf dem Board. Dies kann nicht rückgängig gemacht werden."
1639
+ },
1635
1640
  "service": {
1636
1641
  "title": "Diesen Service löschen?",
1637
1642
  "body": "\"{name}\" und alles darin wird entfernt. Dies kann nicht rückgängig gemacht werden."
@@ -1653,8 +1658,6 @@
1653
1658
  "lastIncorporatedRequirements": "Zuletzt eingearbeitete Anforderungen",
1654
1659
  "priorDocHint": "Aus der vorherigen Prüfung. Verwende es als Basis: bearbeite die Beschreibung oben und reiche erneut ein.",
1655
1660
  "bootstrapping": "Wird initialisiert…",
1656
- "importIssue": "Issue importieren",
1657
- "connectTracker": "Einen Tracker verbinden",
1658
1661
  "spawnFromDocument": "Aus Dokument erzeugen",
1659
1662
  "viewRequirements": "Anforderungen anzeigen",
1660
1663
  "reRun": "Erneut ausführen",
@@ -1665,8 +1668,7 @@
1665
1668
  "title": "Diesen Dienst archivieren?",
1666
1669
  "body": "„{name}“ und seine Aufgaben werden vom Board ausgeblendet. Du kannst den Dienst jederzeit wiederherstellen."
1667
1670
  },
1668
- "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}",
1669
- "huntBugs": "Fehler jagen"
1671
+ "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}"
1670
1672
  }
1671
1673
  },
1672
1674
  "layout": {
@@ -2441,6 +2443,7 @@
2441
2443
  "dragToResize": "Zum Ändern der Größe ziehen",
2442
2444
  "addFirstTask": "Die erste Aufgabe hinzufügen",
2443
2445
  "createInitiativeTitle": "Initiative erstellen",
2446
+ "huntBugsTitle": "Fehler jagen",
2444
2447
  "accessDenied": {
2445
2448
  "title": "Zugriff verweigert",
2446
2449
  "hint": "Sie haben keinen Zugriff auf das Repository, das diesem Service zugrunde liegt."
@@ -4208,6 +4211,8 @@
4208
4211
  "unanswered": "Unbeantwortete Fragen: {count}",
4209
4212
  "proceed": "Jetzt planen",
4210
4213
  "proceedTitle": "Die restlichen Fragen überspringen und den Plan jetzt entwerfen",
4214
+ "discard": "Lauf verwerfen",
4215
+ "discardTitle": "Den blockierten Planungslauf verwerfen, um die Planung neu zu starten",
4211
4216
  "continue": "Antworten senden",
4212
4217
  "continueTitle": "Ihre Antworten senden; der Planer stellt möglicherweise Rückfragen"
4213
4218
  },
@@ -433,6 +433,7 @@
433
433
  "dragToResize": "Drag to resize",
434
434
  "addFirstTask": "Add the first task",
435
435
  "createInitiativeTitle": "Create initiative",
436
+ "huntBugsTitle": "Hunt bugs",
436
437
  "accessDenied": {
437
438
  "title": "Permission denied",
438
439
  "hint": "You don't have access to the repository backing this service."
@@ -694,12 +695,12 @@
694
695
  "inspector": {
695
696
  "detectRepoUnresolved": "This block's repository isn't among the connected GitHub repos, so it can't be read. Connect or sync GitHub for it, then retry.",
696
697
  "container": {
697
- "title": "Contents",
698
- "hint": "The modules and tasks inside this block. Click an entry to select it on the board.",
699
- "modules": "Modules ({count})",
698
+ "modulesTitle": "Modules",
699
+ "modulesHint": "The modules inside this service. Click one to select it on the board.",
700
700
  "taskCount": "{count} task | {count} tasks",
701
- "allTasks": "All tasks ({count})",
702
- "tasks": "Tasks ({count})",
701
+ "tasksTitle": "Tasks",
702
+ "allTasksTitle": "All tasks",
703
+ "tasksHint": "The tasks inside this block. Click one to select it on the board.",
703
704
  "addTask": "Add task",
704
705
  "noTasks": "No tasks yet. Add one to start work.",
705
706
  "servicesHint": "Services are long-lived and don't complete. Work happens in their tasks and modules."
@@ -1005,7 +1006,7 @@
1005
1006
  },
1006
1007
  "execution": {
1007
1008
  "title": "Execution",
1008
- "hint": "The live run of this task's pipeline: each agent step, its progress, and the resulting pull request.",
1009
+ "hint": "The live pipeline run: each agent step, its progress, and the resulting pull request.",
1009
1010
  "stage": {
1010
1011
  "incorporating": "Incorporating…",
1011
1012
  "reviewing": "Re-reviewing…",
@@ -1030,10 +1031,10 @@
1030
1031
  "stop": "Stop",
1031
1032
  "stopTooltip": "Stop the run but keep it (readable and retryable)",
1032
1033
  "reset": "Reset",
1033
- "resetTooltip": "Discard this run and reset the task to planned",
1034
+ "resetTooltip": "Discard this run and reset the status to planned",
1034
1035
  "resetConfirm": {
1035
1036
  "title": "Discard this run?",
1036
- "body": "This deletes the run and returns the task to planned. This can't be undone.",
1037
+ "body": "This deletes the run and resets the status to planned. This can't be undone.",
1037
1038
  "confirm": "Discard run"
1038
1039
  },
1039
1040
  "mergeConfirm": {
@@ -1368,6 +1369,7 @@
1368
1369
  "deleteRecurringPipeline": "Delete recurring pipeline",
1369
1370
  "deleteTask": "Delete task",
1370
1371
  "deleteModule": "Delete module",
1372
+ "deleteInitiative": "Delete initiative",
1371
1373
  "deleteService": "Delete service",
1372
1374
  "confirmDelete": {
1373
1375
  "task": {
@@ -1378,6 +1380,10 @@
1378
1380
  "title": "Delete this module?",
1379
1381
  "body": "\"{name}\" and everything inside it will be removed. This can't be undone."
1380
1382
  },
1383
+ "initiative": {
1384
+ "title": "Delete this initiative?",
1385
+ "body": "\"{name}\" and its plan will be removed. Tasks it already created stay on the board. This can't be undone."
1386
+ },
1381
1387
  "service": {
1382
1388
  "title": "Delete this service?",
1383
1389
  "body": "\"{name}\" and everything inside it will be removed. This can't be undone."
@@ -1399,8 +1405,6 @@
1399
1405
  "lastIncorporatedRequirements": "Last incorporated requirements",
1400
1406
  "priorDocHint": "From the previous review. Use it as a base: edit the description above and resubmit.",
1401
1407
  "bootstrapping": "Bootstrapping…",
1402
- "importIssue": "Import issue",
1403
- "connectTracker": "Connect a tracker",
1404
1408
  "spawnFromDocument": "Spawn from document",
1405
1409
  "viewRequirements": "View Requirements",
1406
1410
  "reRun": "Re-run",
@@ -1414,8 +1418,7 @@
1414
1418
  "runBlocked": "Blocked by an unfinished dependency: {names} | Blocked by {count} unfinished dependencies: {names}",
1415
1419
  "@runBlocked": {
1416
1420
  "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."
1417
- },
1418
- "huntBugs": "Hunt bugs"
1421
+ }
1419
1422
  }
1420
1423
  },
1421
1424
  "observability": {
@@ -5382,6 +5385,8 @@
5382
5385
  "unanswered": "Unanswered questions: {count}",
5383
5386
  "proceed": "Plan now",
5384
5387
  "proceedTitle": "Skip the remaining questions and draft the plan now",
5388
+ "discard": "Discard run",
5389
+ "discardTitle": "Discard the stalled planning run so you can start planning again",
5385
5390
  "continue": "Submit answers",
5386
5391
  "continueTitle": "Send your answers; the planner may ask follow-up questions"
5387
5392
  },
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Arrastra para cambiar el tamaño",
395
395
  "addFirstTask": "Añade la primera tarea",
396
396
  "createInitiativeTitle": "Crear iniciativa",
397
+ "huntBugsTitle": "Cazar errores",
397
398
  "accessDenied": {
398
399
  "title": "Permiso denegado",
399
400
  "hint": "No tienes acceso al repositorio que respalda este servicio."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "El repositorio de este bloque no está entre los repositorios de GitHub conectados, por lo que no se puede leer. Conecta o sincroniza GitHub para él y vuelve a intentarlo.",
633
634
  "container": {
634
- "title": "Contenido",
635
- "hint": "Los módulos y tareas dentro de este bloque. Haz clic en una entrada para seleccionarla en el tablero.",
636
- "modules": "Módulos ({count})",
635
+ "modulesTitle": "Módulos",
636
+ "modulesHint": "Los módulos dentro de este servicio. Haz clic en uno para seleccionarlo en el tablero.",
637
637
  "taskCount": "{count} tarea | {count} tareas",
638
- "allTasks": "Todas las tareas ({count})",
639
- "tasks": "Tareas ({count})",
638
+ "tasksTitle": "Tareas",
639
+ "allTasksTitle": "Todas las tareas",
640
+ "tasksHint": "Las tareas dentro de este bloque. Haz clic en una para seleccionarla en el tablero.",
640
641
  "addTask": "Añadir tarea",
641
642
  "noTasks": "Aún no hay tareas. Añade una para empezar a trabajar.",
642
643
  "servicesHint": "Los servicios son de larga duración y no se completan. El trabajo ocurre en sus tareas y módulos."
@@ -942,7 +943,7 @@
942
943
  },
943
944
  "execution": {
944
945
  "title": "Ejecución",
945
- "hint": "La ejecución en vivo del pipeline de esta tarea: cada paso de agente, su progreso y el pull request resultante.",
946
+ "hint": "La ejecución en vivo del pipeline: cada paso de agente, su progreso y el pull request resultante.",
946
947
  "stage": {
947
948
  "incorporating": "Incorporando…",
948
949
  "reviewing": "Revisando de nuevo…",
@@ -963,10 +964,10 @@
963
964
  "stop": "Detener",
964
965
  "stopTooltip": "Detener la ejecución pero conservarla (legible y reintentable)",
965
966
  "reset": "Restablecer",
966
- "resetTooltip": "Descartar esta ejecución y restablecer la tarea a planificada",
967
+ "resetTooltip": "Descartar esta ejecución y restablecer el estado a planificada",
967
968
  "resetConfirm": {
968
969
  "title": "¿Descartar esta ejecución?",
969
- "body": "Esto elimina la ejecución y devuelve la tarea a planificada. Esto no se puede deshacer.",
970
+ "body": "Esto elimina la ejecución y restablece el estado a planificada. Esto no se puede deshacer.",
970
971
  "confirm": "Descartar ejecución"
971
972
  },
972
973
  "mergeConfirm": {
@@ -1305,6 +1306,7 @@
1305
1306
  "deleteRecurringPipeline": "Eliminar pipeline recurrente",
1306
1307
  "deleteTask": "Eliminar tarea",
1307
1308
  "deleteModule": "Eliminar módulo",
1309
+ "deleteInitiative": "Eliminar iniciativa",
1308
1310
  "deleteService": "Eliminar servicio",
1309
1311
  "titlePlaceholder": "Título…",
1310
1312
  "reworkedRequirements": "Requisitos reelaborados",
@@ -1317,8 +1319,6 @@
1317
1319
  "lastIncorporatedRequirements": "Últimos requisitos incorporados",
1318
1320
  "priorDocHint": "De la revisión anterior. Úsalo como base: edita la descripción de arriba y reenvíala.",
1319
1321
  "bootstrapping": "Inicializando…",
1320
- "importIssue": "Importar incidencia",
1321
- "connectTracker": "Conectar un tracker",
1322
1322
  "spawnFromDocument": "Generar desde documento",
1323
1323
  "viewRequirements": "Ver requisitos",
1324
1324
  "reRun": "Reejecutar",
@@ -1333,6 +1333,10 @@
1333
1333
  "title": "¿Eliminar este módulo?",
1334
1334
  "body": "Se eliminará \"{name}\" y todo su contenido. Esta acción no se puede deshacer."
1335
1335
  },
1336
+ "initiative": {
1337
+ "title": "¿Eliminar esta iniciativa?",
1338
+ "body": "Se eliminarán \"{name}\" y su plan. Las tareas que ya creó permanecen en el tablero. Esta acción no se puede deshacer."
1339
+ },
1336
1340
  "service": {
1337
1341
  "title": "¿Eliminar este servicio?",
1338
1342
  "body": "Se eliminará \"{name}\" y todo su contenido. Esta acción no se puede deshacer."
@@ -1348,8 +1352,7 @@
1348
1352
  "title": "¿Archivar este servicio?",
1349
1353
  "body": "«{name}» y sus tareas se ocultarán del tablero. Puedes restaurarlo en cualquier momento."
1350
1354
  },
1351
- "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}",
1352
- "huntBugs": "Cazar errores"
1355
+ "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}"
1353
1356
  }
1354
1357
  },
1355
1358
  "observability": {
@@ -5217,6 +5220,8 @@
5217
5220
  "unanswered": "Preguntas sin responder: {count}",
5218
5221
  "proceed": "Planificar ahora",
5219
5222
  "proceedTitle": "Omitir las preguntas restantes y redactar el plan ahora",
5223
+ "discard": "Descartar ejecución",
5224
+ "discardTitle": "Descartar la ejecución de planificación bloqueada para volver a empezar",
5220
5225
  "continue": "Enviar respuestas",
5221
5226
  "continueTitle": "Envia tus respuestas; el planificador puede hacer preguntas de seguimiento"
5222
5227
  },