@cat-factory/app 0.111.2 → 0.112.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.
- package/app/components/forkDecision/ForkDecisionWindow.vue +300 -0
- package/app/components/layout/NotificationsInbox.vue +15 -0
- package/app/components/panels/StepResultViewHost.vue +5 -0
- package/app/components/panels/inspector/TaskExecution.vue +21 -0
- package/app/components/pipeline/PipelineProgress.vue +41 -1
- package/app/components/settings/RiskPolicyPanel.vue +96 -0
- package/app/components/slack/SlackPanel.vue +1 -0
- package/app/composables/api/forkDecision.ts +29 -0
- package/app/composables/useApi.ts +2 -0
- package/app/stores/execution.spec.ts +52 -0
- package/app/stores/execution.ts +31 -2
- package/app/stores/forkDecision.ts +84 -0
- package/app/stores/ui.ts +26 -0
- package/app/types/execution.ts +5 -0
- package/app/utils/catalog.ts +11 -0
- package/i18n/locales/de.json +50 -3
- package/i18n/locales/en.json +50 -3
- package/i18n/locales/es.json +50 -3
- package/i18n/locales/fr.json +50 -3
- package/i18n/locales/he.json +50 -3
- package/i18n/locales/it.json +50 -3
- package/i18n/locales/ja.json +50 -3
- package/i18n/locales/pl.json +50 -3
- package/i18n/locales/tr.json +50 -3
- package/i18n/locales/uk.json +50 -3
- package/package.json +2 -2
|
@@ -134,3 +134,55 @@ describe('execution store snapshot/event reconcile', () => {
|
|
|
134
134
|
expect(store.getInstance('e1')?.status).toBe('done')
|
|
135
135
|
})
|
|
136
136
|
})
|
|
137
|
+
|
|
138
|
+
/** A run whose steps carry an (optional) per-step metrics rollup. */
|
|
139
|
+
function runWithMetrics(
|
|
140
|
+
id: string,
|
|
141
|
+
rev: number,
|
|
142
|
+
steps: Array<{ agentKind: string; metrics?: { calls: number } | null }>,
|
|
143
|
+
): ExecutionInstance {
|
|
144
|
+
return { id, blockId: `blk_${id}`, steps, status: 'running', rev } as unknown as ExecutionInstance
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
describe('execution store metrics preservation (live-only rollup)', () => {
|
|
148
|
+
let store: ReturnType<typeof useExecutionStore>
|
|
149
|
+
beforeEach(() => {
|
|
150
|
+
store = useExecutionStore()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('a metric-less running-fold event does not blank the last-known step metrics', () => {
|
|
154
|
+
// A step-boundary emit carried the rollup...
|
|
155
|
+
store.upsert(runWithMetrics('e1', 1, [{ agentKind: 'coder', metrics: { calls: 3 } }]))
|
|
156
|
+
// ...a later progress-only fold (higher rev) omits it — the backend skips the rollup there.
|
|
157
|
+
store.upsert(runWithMetrics('e1', 2, [{ agentKind: 'coder' }]))
|
|
158
|
+
const step = store.getInstance('e1')!.steps[0] as unknown as { metrics?: { calls: number } }
|
|
159
|
+
expect(step.metrics?.calls).toBe(3)
|
|
160
|
+
expect(store.getInstance('e1')?.rev).toBe(2) // the fold still won (progress/subtasks applied)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('a fresh rollup overrides the preserved value', () => {
|
|
164
|
+
store.upsert(runWithMetrics('e1', 1, [{ agentKind: 'coder', metrics: { calls: 3 } }]))
|
|
165
|
+
store.upsert(runWithMetrics('e1', 2, [{ agentKind: 'coder' }])) // fold: preserved
|
|
166
|
+
store.upsert(runWithMetrics('e1', 3, [{ agentKind: 'coder', metrics: { calls: 7 } }]))
|
|
167
|
+
const step = store.getInstance('e1')!.steps[0] as unknown as { metrics?: { calls: number } }
|
|
168
|
+
expect(step.metrics?.calls).toBe(7)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('does not carry metrics across a reshaped step (agentKind mismatch at the index)', () => {
|
|
172
|
+
store.upsert(runWithMetrics('e1', 1, [{ agentKind: 'coder', metrics: { calls: 3 } }]))
|
|
173
|
+
// A different kind at index 0 must not inherit the coder's rollup.
|
|
174
|
+
store.upsert(runWithMetrics('e1', 2, [{ agentKind: 'reviewer' }]))
|
|
175
|
+
const step = store.getInstance('e1')!.steps[0] as unknown as { metrics?: { calls: number } }
|
|
176
|
+
expect(step.metrics).toBeUndefined()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('preserves metrics through a lagging full refresh that omits them (hydrate)', () => {
|
|
180
|
+
// Establish the workspace first (a fresh-workspace hydrate replaces outright by design).
|
|
181
|
+
store.hydrate([runWithMetrics('e1', 1, [{ agentKind: 'coder' }])], 'ws1')
|
|
182
|
+
store.upsert(runWithMetrics('e1', 2, [{ agentKind: 'coder', metrics: { calls: 5 } }]))
|
|
183
|
+
// A snapshot never carries metrics (never persisted); a same-rev refresh must not blank it.
|
|
184
|
+
store.hydrate([runWithMetrics('e1', 2, [{ agentKind: 'coder' }])], 'ws1')
|
|
185
|
+
const step = store.getInstance('e1')!.steps[0] as unknown as { metrics?: { calls: number } }
|
|
186
|
+
expect(step.metrics?.calls).toBe(5)
|
|
187
|
+
})
|
|
188
|
+
})
|
package/app/stores/execution.ts
CHANGED
|
@@ -40,6 +40,33 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
40
40
|
return status === 'done' || status === 'failed'
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Carry forward each step's LLM-metrics rollup (`step.metrics`) when an incoming
|
|
45
|
+
* instance omits it. Metrics is DERIVED, LIVE-ONLY state: the backend attaches it only
|
|
46
|
+
* on step-boundary/terminal emits (not on the frequent progress-only running folds — a
|
|
47
|
+
* perf optimisation that skips the per-run metrics GROUP BY on every poll tick) and
|
|
48
|
+
* never persists it, so it rides neither the snapshot nor a running-fold event. A plain
|
|
49
|
+
* REPLACE would blank the per-step metrics bar on every progress tick; per the live-push
|
|
50
|
+
* coherence rules a REPLACE must not drop live-only state, so preserve the last-known
|
|
51
|
+
* rollup per step. Steps are positionally stable within a run (same id ⇒ same shape), so
|
|
52
|
+
* match by index; the agentKind guard is belt-and-suspenders against a reshaped list.
|
|
53
|
+
*/
|
|
54
|
+
function withPreservedMetrics(
|
|
55
|
+
incoming: ExecutionInstance,
|
|
56
|
+
cached: ExecutionInstance | undefined,
|
|
57
|
+
): ExecutionInstance {
|
|
58
|
+
if (!cached) return incoming
|
|
59
|
+
let changed = false
|
|
60
|
+
const steps = incoming.steps.map((step, i) => {
|
|
61
|
+
if (step.metrics != null) return step
|
|
62
|
+
const prior = cached.steps[i]
|
|
63
|
+
if (prior?.metrics == null || prior.agentKind !== step.agentKind) return step
|
|
64
|
+
changed = true
|
|
65
|
+
return { ...step, metrics: prior.metrics }
|
|
66
|
+
})
|
|
67
|
+
return changed ? { ...incoming, steps } : incoming
|
|
68
|
+
}
|
|
69
|
+
|
|
43
70
|
/**
|
|
44
71
|
* Reconcile the cached executions with a server snapshot for `workspaceId`. A snapshot
|
|
45
72
|
* is authoritative EXCEPT where a live `execution` event already advanced (or ADDED) a
|
|
@@ -81,7 +108,8 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
81
108
|
const held = new Map(instances.value.map((e) => [e.id, e]))
|
|
82
109
|
const reconciled = next.map((incoming) => {
|
|
83
110
|
const current = held.get(incoming.id)
|
|
84
|
-
|
|
111
|
+
if (current && revOf(current) > revOf(incoming)) return current
|
|
112
|
+
return withPreservedMetrics(incoming, current)
|
|
85
113
|
})
|
|
86
114
|
// Preserve a cached-only run UNLESS it is the terminal predecessor a retry replaced: a
|
|
87
115
|
// finished (`done`/`failed`) run whose block the snapshot now covers under a fresh id.
|
|
@@ -101,7 +129,8 @@ export const useExecutionStore = defineStore('execution', () => {
|
|
|
101
129
|
function upsert(instance: ExecutionInstance) {
|
|
102
130
|
const i = instances.value.findIndex((e) => e.id === instance.id)
|
|
103
131
|
if (i >= 0) {
|
|
104
|
-
if (revOf(instance) >= revOf(instances.value[i]!))
|
|
132
|
+
if (revOf(instance) >= revOf(instances.value[i]!))
|
|
133
|
+
instances.value[i] = withPreservedMetrics(instance, instances.value[i]!)
|
|
105
134
|
} else instances.value.push(instance)
|
|
106
135
|
}
|
|
107
136
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import type { ForkDecisionStepState } from '~/types/execution'
|
|
4
|
+
import { useApi } from '~/composables/useApi'
|
|
5
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
6
|
+
import { useExecutionStore } from '~/stores/execution'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The implementation-fork decision action surface. The live fork state lives on the run's
|
|
10
|
+
* Coder step (`step.forkDecision`) and is kept fresh by the execution stream, so the window
|
|
11
|
+
* reads it straight off the execution store — this store only wraps the `choose` action (and
|
|
12
|
+
* a warm-up `load`), tracks the in-flight state so the window can disable its controls, and
|
|
13
|
+
* reflects the returned state back onto the execution store so the UI updates immediately even
|
|
14
|
+
* before the stream echoes the change. Keyed by executionId, mirroring the follow-ups store.
|
|
15
|
+
*/
|
|
16
|
+
export const useForkDecisionStore = defineStore('forkDecision', () => {
|
|
17
|
+
const api = useApi()
|
|
18
|
+
const workspace = useWorkspaceStore()
|
|
19
|
+
const execution = useExecutionStore()
|
|
20
|
+
|
|
21
|
+
/** True while a choose call is in flight (drives the Choose button spinner / disabled state). */
|
|
22
|
+
const choosing = ref(false)
|
|
23
|
+
/** The last error message from an action, surfaced inline; cleared on the next action. */
|
|
24
|
+
const error = ref<string | null>(null)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reflect an authoritative fork-decision state onto the run's Coder step. A pipeline may
|
|
28
|
+
* carry more than one `coder` step, so target the step this decision is about rather than
|
|
29
|
+
* the first one that happens to hold fork state: prefer the step that is still live
|
|
30
|
+
* (proposing / awaiting the choice / answering), then the current step, and only then fall
|
|
31
|
+
* back to the first step carrying fork state. The stream corrects any mismatch, but this
|
|
32
|
+
* keeps the immediate optimistic echo on the right step.
|
|
33
|
+
*/
|
|
34
|
+
function reflect(executionId: string, state: ForkDecisionStepState | null): void {
|
|
35
|
+
if (!state) return
|
|
36
|
+
const instance = execution.getInstance(executionId)
|
|
37
|
+
if (!instance) return
|
|
38
|
+
const isLive = (s: (typeof instance.steps)[number]) =>
|
|
39
|
+
s.agentKind === 'coder' &&
|
|
40
|
+
(s.forkDecision?.status === 'awaiting_choice' ||
|
|
41
|
+
s.forkDecision?.status === 'answering' ||
|
|
42
|
+
s.forkDecision?.status === 'proposing')
|
|
43
|
+
const current = instance.steps[instance.currentStep]
|
|
44
|
+
const step =
|
|
45
|
+
instance.steps.find(isLive) ??
|
|
46
|
+
(current?.agentKind === 'coder' && current.forkDecision ? current : undefined) ??
|
|
47
|
+
instance.steps.find((s) => s.forkDecision)
|
|
48
|
+
if (step) step.forkDecision = state
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Warm the live state from the GET (the stream also keeps it fresh). Best-effort. */
|
|
52
|
+
async function load(executionId: string): Promise<void> {
|
|
53
|
+
error.value = null
|
|
54
|
+
try {
|
|
55
|
+
const state = await api.getForkDecision(workspace.requireId(), executionId)
|
|
56
|
+
reflect(executionId, state as ForkDecisionStepState | null)
|
|
57
|
+
} catch (e) {
|
|
58
|
+
error.value = e instanceof Error ? e.message : 'Failed to load'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Choose an implementation approach: a proposed fork id OR a custom free-text approach
|
|
64
|
+
* (with an optional steering note). The Coder then re-runs with the choice folded in.
|
|
65
|
+
*/
|
|
66
|
+
async function choose(
|
|
67
|
+
executionId: string,
|
|
68
|
+
choice: { forkId?: string; custom?: string; note?: string },
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
error.value = null
|
|
71
|
+
choosing.value = true
|
|
72
|
+
try {
|
|
73
|
+
const state = await api.chooseFork(workspace.requireId(), executionId, choice)
|
|
74
|
+
reflect(executionId, state as ForkDecisionStepState)
|
|
75
|
+
} catch (e) {
|
|
76
|
+
error.value = e instanceof Error ? e.message : 'Failed to choose'
|
|
77
|
+
throw e
|
|
78
|
+
} finally {
|
|
79
|
+
choosing.value = false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { choosing, error, load, choose }
|
|
84
|
+
})
|
package/app/stores/ui.ts
CHANGED
|
@@ -809,6 +809,31 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
809
809
|
stepIndex: idx,
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
|
+
// Open the implementation-fork decision window for a run's coder step (from the inspector /
|
|
813
|
+
// pipeline chip / `fork_decision_pending` notification). Resolves the coder step index from
|
|
814
|
+
// the run when not given, preferring the step parked awaiting a choice.
|
|
815
|
+
function openForkDecision(instanceId: string, stepIndex: number | null = null) {
|
|
816
|
+
const execution = useExecutionStore()
|
|
817
|
+
const instance = execution.getInstance(instanceId)
|
|
818
|
+
if (!instance) return
|
|
819
|
+
const resolveIdx = () => {
|
|
820
|
+
const awaiting = instance.steps.findIndex(
|
|
821
|
+
(s) => s.agentKind === 'coder' && s.forkDecision?.status === 'awaiting_choice',
|
|
822
|
+
)
|
|
823
|
+
if (awaiting >= 0) return awaiting
|
|
824
|
+
const current = instance.steps[instance.currentStep]
|
|
825
|
+
if (current?.agentKind === 'coder' && current.forkDecision) return instance.currentStep
|
|
826
|
+
return instance.steps.findIndex((s) => s.agentKind === 'coder' && s.forkDecision)
|
|
827
|
+
}
|
|
828
|
+
const idx = stepIndex ?? resolveIdx()
|
|
829
|
+
if (idx < 0) return
|
|
830
|
+
resultView.value = {
|
|
831
|
+
view: 'fork-decision',
|
|
832
|
+
blockId: instance.blockId,
|
|
833
|
+
instanceId,
|
|
834
|
+
stepIndex: idx,
|
|
835
|
+
}
|
|
836
|
+
}
|
|
812
837
|
function closeResultView() {
|
|
813
838
|
resultView.value = null
|
|
814
839
|
}
|
|
@@ -1006,6 +1031,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
1006
1031
|
openInitiativeTracker,
|
|
1007
1032
|
openInitiativePlanning,
|
|
1008
1033
|
openFollowUps,
|
|
1034
|
+
openForkDecision,
|
|
1009
1035
|
closeRequirementReview,
|
|
1010
1036
|
openStepDetail,
|
|
1011
1037
|
closeStepDetail,
|
package/app/types/execution.ts
CHANGED
package/app/utils/catalog.ts
CHANGED
|
@@ -708,6 +708,17 @@ export const FOLLOW_UP_COMPANION_META = {
|
|
|
708
708
|
color: '#f472b6',
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
+
/**
|
|
712
|
+
* Display metadata for the implementation-fork decision phase on a Coder step (a per-step
|
|
713
|
+
* phase, not an agent kind of its own — the read-only `fork-proposer` is never a palette
|
|
714
|
+
* block). Drives the fork-decision window header + the pipeline phase chip.
|
|
715
|
+
*/
|
|
716
|
+
export const FORK_DECISION_META = {
|
|
717
|
+
label: 'Implementation-fork decision',
|
|
718
|
+
icon: 'i-lucide-git-fork',
|
|
719
|
+
color: '#a78bfa',
|
|
720
|
+
}
|
|
721
|
+
|
|
711
722
|
/**
|
|
712
723
|
* Whether a Coder step has the Follow-up companion enabled, given the pipeline's per-step
|
|
713
724
|
* `followUps` toggle at index `i`. Enabled by default on a `coder` step (only `false`
|
package/i18n/locales/de.json
CHANGED
|
@@ -438,6 +438,18 @@
|
|
|
438
438
|
"createFailed": "Richtlinie konnte nicht erstellt werden",
|
|
439
439
|
"defaultFailed": "Standard konnte nicht festgelegt werden",
|
|
440
440
|
"deleteFailed": "Richtlinie konnte nicht gelöscht werden"
|
|
441
|
+
},
|
|
442
|
+
"forkDecision": {
|
|
443
|
+
"label": "Implementierungs-Weichenentscheidung",
|
|
444
|
+
"hint": "Im Auto-Modus grundlegend verschiedene Ansätze vorschlagen (und für eine Wahl pausieren), wenn die Aufgabenschätzung einen Schwellenwert erreicht.",
|
|
445
|
+
"minComplexity": "Min. Komplexität",
|
|
446
|
+
"minRisk": "Min. Risiko",
|
|
447
|
+
"minImpact": "Min. Auswirkung",
|
|
448
|
+
"onMissingLabel": "Keine Schätzung",
|
|
449
|
+
"onMissing": {
|
|
450
|
+
"run": "Trotzdem vorschlagen",
|
|
451
|
+
"skip": "Überspringen"
|
|
452
|
+
}
|
|
441
453
|
}
|
|
442
454
|
},
|
|
443
455
|
"observabilityConnection": {
|
|
@@ -1161,7 +1173,8 @@
|
|
|
1161
1173
|
"companionOf": "{label} (Begleiter)",
|
|
1162
1174
|
"merged": "Gemergt",
|
|
1163
1175
|
"open": "Öffnen",
|
|
1164
|
-
"mergePr": "PR mergen"
|
|
1176
|
+
"mergePr": "PR mergen",
|
|
1177
|
+
"chooseApproach": "Ansatz wählen"
|
|
1165
1178
|
},
|
|
1166
1179
|
"structure": {
|
|
1167
1180
|
"title": "Struktur",
|
|
@@ -1649,7 +1662,8 @@
|
|
|
1649
1662
|
"human_review": "Als gelesen markieren",
|
|
1650
1663
|
"followup_pending": "Als gelesen markieren",
|
|
1651
1664
|
"initiative": "Als gelesen markieren",
|
|
1652
|
-
"markRead": "Als gelesen markieren"
|
|
1665
|
+
"markRead": "Als gelesen markieren",
|
|
1666
|
+
"fork_decision_pending": "Als gelesen markieren"
|
|
1653
1667
|
}
|
|
1654
1668
|
},
|
|
1655
1669
|
"aiProvidersBanner": {
|
|
@@ -2979,7 +2993,11 @@
|
|
|
2979
2993
|
"subtasksInProgress": "· {count} in Arbeit",
|
|
2980
2994
|
"clickToRead": "Klicken, um die Ausgabe dieses Agenten zu lesen",
|
|
2981
2995
|
"reviewApprove": "Vorschlag von {agent} prüfen & freigeben",
|
|
2982
|
-
"resolve": "Klären: {question}"
|
|
2996
|
+
"resolve": "Klären: {question}",
|
|
2997
|
+
"forkDecision": {
|
|
2998
|
+
"proposing": "Ansätze werden vorgeschlagen…",
|
|
2999
|
+
"choose": "Ansatz wählen"
|
|
3000
|
+
}
|
|
2983
3001
|
},
|
|
2984
3002
|
"health": {
|
|
2985
3003
|
"title": "Pipeline-Zustand",
|
|
@@ -4551,5 +4569,34 @@
|
|
|
4551
4569
|
"palette": {
|
|
4552
4570
|
"hint": "Klicke auf einen Agenten, um ihn an die Pipeline anzuhängen.",
|
|
4553
4571
|
"customAgents": "Benutzerdefinierte Agenten"
|
|
4572
|
+
},
|
|
4573
|
+
"forkDecision": {
|
|
4574
|
+
"title": "Implementierungsansatz wählen",
|
|
4575
|
+
"titleWithBlock": "Ansatz für {title} wählen",
|
|
4576
|
+
"subtitle": "Grundlegend verschiedene Wege, diese Aufgabe umzusetzen, bevor Code geschrieben wird.",
|
|
4577
|
+
"seam": "Betroffene Stelle:",
|
|
4578
|
+
"recommended": "Empfohlen",
|
|
4579
|
+
"riskNotes": "Risiko:",
|
|
4580
|
+
"noteLabel": "Hinweis (optional)",
|
|
4581
|
+
"notePlaceholder": "Was die Umsetzung berücksichtigen soll…",
|
|
4582
|
+
"choose": "Diesen Ansatz verwenden",
|
|
4583
|
+
"proposing": {
|
|
4584
|
+
"title": "Ansätze werden ermittelt…",
|
|
4585
|
+
"hint": "Der Code wird gelesen, um die grundlegend verschiedenen Umsetzungswege zu finden."
|
|
4586
|
+
},
|
|
4587
|
+
"singlePath": {
|
|
4588
|
+
"title": "Ein klarer Ansatz"
|
|
4589
|
+
},
|
|
4590
|
+
"chosen": {
|
|
4591
|
+
"title": "Ansatz gewählt",
|
|
4592
|
+
"note": "Hinweis: {note}"
|
|
4593
|
+
},
|
|
4594
|
+
"custom": {
|
|
4595
|
+
"title": "Eigenen Ansatz eingeben",
|
|
4596
|
+
"placeholder": "Beschreibe, wie dies umgesetzt werden soll…"
|
|
4597
|
+
},
|
|
4598
|
+
"empty": {
|
|
4599
|
+
"title": "Nichts zu entscheiden"
|
|
4600
|
+
}
|
|
4554
4601
|
}
|
|
4555
4602
|
}
|
package/i18n/locales/en.json
CHANGED
|
@@ -911,7 +911,8 @@
|
|
|
911
911
|
"companionOf": "{label} (companion)",
|
|
912
912
|
"merged": "Merged",
|
|
913
913
|
"open": "Open",
|
|
914
|
-
"mergePr": "Merge PR"
|
|
914
|
+
"mergePr": "Merge PR",
|
|
915
|
+
"chooseApproach": "Choose approach"
|
|
915
916
|
},
|
|
916
917
|
"structure": {
|
|
917
918
|
"title": "Structure",
|
|
@@ -1539,7 +1540,8 @@
|
|
|
1539
1540
|
"human_review": "Mark read",
|
|
1540
1541
|
"followup_pending": "Mark read",
|
|
1541
1542
|
"initiative": "Mark read",
|
|
1542
|
-
"markRead": "Mark read"
|
|
1543
|
+
"markRead": "Mark read",
|
|
1544
|
+
"fork_decision_pending": "Mark read"
|
|
1543
1545
|
}
|
|
1544
1546
|
},
|
|
1545
1547
|
"aiProvidersBanner": {
|
|
@@ -2213,6 +2215,18 @@
|
|
|
2213
2215
|
"createFailed": "Could not create policy",
|
|
2214
2216
|
"defaultFailed": "Could not set default",
|
|
2215
2217
|
"deleteFailed": "Could not delete policy"
|
|
2218
|
+
},
|
|
2219
|
+
"forkDecision": {
|
|
2220
|
+
"label": "Implementation-fork decision",
|
|
2221
|
+
"hint": "In auto mode, propose materially different approaches (and pause for a choice) when the task estimate meets a threshold.",
|
|
2222
|
+
"minComplexity": "Min complexity",
|
|
2223
|
+
"minRisk": "Min risk",
|
|
2224
|
+
"minImpact": "Min impact",
|
|
2225
|
+
"onMissingLabel": "No estimate",
|
|
2226
|
+
"onMissing": {
|
|
2227
|
+
"run": "Propose anyway",
|
|
2228
|
+
"skip": "Skip"
|
|
2229
|
+
}
|
|
2216
2230
|
}
|
|
2217
2231
|
},
|
|
2218
2232
|
"observabilityConnection": {
|
|
@@ -3291,7 +3305,11 @@
|
|
|
3291
3305
|
"subtasksInProgress": "· {count} in progress",
|
|
3292
3306
|
"clickToRead": "Click to read this agent's output",
|
|
3293
3307
|
"reviewApprove": "Review & approve {agent}'s proposal",
|
|
3294
|
-
"resolve": "Resolve: {question}"
|
|
3308
|
+
"resolve": "Resolve: {question}",
|
|
3309
|
+
"forkDecision": {
|
|
3310
|
+
"proposing": "Proposing approaches…",
|
|
3311
|
+
"choose": "Choose an approach"
|
|
3312
|
+
}
|
|
3295
3313
|
},
|
|
3296
3314
|
"health": {
|
|
3297
3315
|
"title": "Pipeline health",
|
|
@@ -4668,5 +4686,34 @@
|
|
|
4668
4686
|
"hint": "Optionally provision this environment now to test the recipe.",
|
|
4669
4687
|
"run": "Trial provision"
|
|
4670
4688
|
}
|
|
4689
|
+
},
|
|
4690
|
+
"forkDecision": {
|
|
4691
|
+
"title": "Choose an implementation approach",
|
|
4692
|
+
"titleWithBlock": "Choose an approach for {title}",
|
|
4693
|
+
"subtitle": "Materially different ways to implement this task, before any code is written.",
|
|
4694
|
+
"seam": "Where it lands:",
|
|
4695
|
+
"recommended": "Recommended",
|
|
4696
|
+
"riskNotes": "Risk:",
|
|
4697
|
+
"noteLabel": "Steering note (optional)",
|
|
4698
|
+
"notePlaceholder": "Anything the implementer should keep in mind…",
|
|
4699
|
+
"choose": "Use this approach",
|
|
4700
|
+
"proposing": {
|
|
4701
|
+
"title": "Surfacing approaches…",
|
|
4702
|
+
"hint": "Reading the code to find the materially different ways to build this."
|
|
4703
|
+
},
|
|
4704
|
+
"singlePath": {
|
|
4705
|
+
"title": "One clear approach"
|
|
4706
|
+
},
|
|
4707
|
+
"chosen": {
|
|
4708
|
+
"title": "Approach chosen",
|
|
4709
|
+
"note": "Note: {note}"
|
|
4710
|
+
},
|
|
4711
|
+
"custom": {
|
|
4712
|
+
"title": "Enter your own approach",
|
|
4713
|
+
"placeholder": "Describe how you want this implemented…"
|
|
4714
|
+
},
|
|
4715
|
+
"empty": {
|
|
4716
|
+
"title": "Nothing to decide"
|
|
4717
|
+
}
|
|
4671
4718
|
}
|
|
4672
4719
|
}
|
package/i18n/locales/es.json
CHANGED
|
@@ -857,7 +857,8 @@
|
|
|
857
857
|
"empty": {
|
|
858
858
|
"title": "Aún no hay ejecuciones",
|
|
859
859
|
"body": "Inicia un pipeline para ver el historial de ejecución aquí."
|
|
860
|
-
}
|
|
860
|
+
},
|
|
861
|
+
"chooseApproach": "Elegir enfoque"
|
|
861
862
|
},
|
|
862
863
|
"structure": {
|
|
863
864
|
"title": "Estructura",
|
|
@@ -1476,7 +1477,8 @@
|
|
|
1476
1477
|
"human_review": "Marcar como leída",
|
|
1477
1478
|
"followup_pending": "Marcar como leída",
|
|
1478
1479
|
"initiative": "Marcar como leída",
|
|
1479
|
-
"markRead": "Marcar como leída"
|
|
1480
|
+
"markRead": "Marcar como leída",
|
|
1481
|
+
"fork_decision_pending": "Marcar como leído"
|
|
1480
1482
|
},
|
|
1481
1483
|
"toast": {
|
|
1482
1484
|
"acted": "Marcado como resuelto",
|
|
@@ -2031,6 +2033,18 @@
|
|
|
2031
2033
|
"confirmDelete": {
|
|
2032
2034
|
"title": "¿Eliminar esta política de riesgo?",
|
|
2033
2035
|
"body": "Se eliminará \"{name}\". Las tareas que lo usan volverán al valor predeterminado del espacio de trabajo."
|
|
2036
|
+
},
|
|
2037
|
+
"forkDecision": {
|
|
2038
|
+
"label": "Decisión de bifurcación de implementación",
|
|
2039
|
+
"hint": "En modo automático, propone enfoques sustancialmente distintos (y se detiene para elegir) cuando la estimación de la tarea alcanza un umbral.",
|
|
2040
|
+
"minComplexity": "Complejidad mín.",
|
|
2041
|
+
"minRisk": "Riesgo mín.",
|
|
2042
|
+
"minImpact": "Impacto mín.",
|
|
2043
|
+
"onMissingLabel": "Sin estimación",
|
|
2044
|
+
"onMissing": {
|
|
2045
|
+
"run": "Proponer igualmente",
|
|
2046
|
+
"skip": "Omitir"
|
|
2047
|
+
}
|
|
2034
2048
|
}
|
|
2035
2049
|
},
|
|
2036
2050
|
"observabilityConnection": {
|
|
@@ -3204,7 +3218,11 @@
|
|
|
3204
3218
|
"subtasksInProgress": "· {count} en progreso",
|
|
3205
3219
|
"clickToRead": "Haz clic para leer la salida de este agente",
|
|
3206
3220
|
"reviewApprove": "Revisar y aprobar la propuesta de {agent}",
|
|
3207
|
-
"resolve": "Resolver: {question}"
|
|
3221
|
+
"resolve": "Resolver: {question}",
|
|
3222
|
+
"forkDecision": {
|
|
3223
|
+
"proposing": "Proponiendo enfoques…",
|
|
3224
|
+
"choose": "Elegir un enfoque"
|
|
3225
|
+
}
|
|
3208
3226
|
},
|
|
3209
3227
|
"health": {
|
|
3210
3228
|
"title": "Estado de los pipelines",
|
|
@@ -4539,5 +4557,34 @@
|
|
|
4539
4557
|
"hint": "Opcionalmente, aprovisiona este entorno ahora para probar la receta.",
|
|
4540
4558
|
"run": "Aprovisionamiento de prueba"
|
|
4541
4559
|
}
|
|
4560
|
+
},
|
|
4561
|
+
"forkDecision": {
|
|
4562
|
+
"title": "Elige un enfoque de implementación",
|
|
4563
|
+
"titleWithBlock": "Elige un enfoque para {title}",
|
|
4564
|
+
"subtitle": "Formas sustancialmente distintas de implementar esta tarea, antes de escribir código.",
|
|
4565
|
+
"seam": "Dónde impacta:",
|
|
4566
|
+
"recommended": "Recomendado",
|
|
4567
|
+
"riskNotes": "Riesgo:",
|
|
4568
|
+
"noteLabel": "Nota de orientación (opcional)",
|
|
4569
|
+
"notePlaceholder": "Algo que la implementación deba tener en cuenta…",
|
|
4570
|
+
"choose": "Usar este enfoque",
|
|
4571
|
+
"proposing": {
|
|
4572
|
+
"title": "Buscando enfoques…",
|
|
4573
|
+
"hint": "Leyendo el código para encontrar las formas sustancialmente distintas de construir esto."
|
|
4574
|
+
},
|
|
4575
|
+
"singlePath": {
|
|
4576
|
+
"title": "Un único enfoque claro"
|
|
4577
|
+
},
|
|
4578
|
+
"chosen": {
|
|
4579
|
+
"title": "Enfoque elegido",
|
|
4580
|
+
"note": "Nota: {note}"
|
|
4581
|
+
},
|
|
4582
|
+
"custom": {
|
|
4583
|
+
"title": "Introduce tu propio enfoque",
|
|
4584
|
+
"placeholder": "Describe cómo quieres que se implemente…"
|
|
4585
|
+
},
|
|
4586
|
+
"empty": {
|
|
4587
|
+
"title": "Nada que decidir"
|
|
4588
|
+
}
|
|
4542
4589
|
}
|
|
4543
4590
|
}
|
package/i18n/locales/fr.json
CHANGED
|
@@ -857,7 +857,8 @@
|
|
|
857
857
|
"empty": {
|
|
858
858
|
"title": "Aucune exécution pour l'instant",
|
|
859
859
|
"body": "Lancez un pipeline pour voir l'historique d'exécution ici."
|
|
860
|
-
}
|
|
860
|
+
},
|
|
861
|
+
"chooseApproach": "Choisir l'approche"
|
|
861
862
|
},
|
|
862
863
|
"structure": {
|
|
863
864
|
"title": "Structure",
|
|
@@ -1476,7 +1477,8 @@
|
|
|
1476
1477
|
"human_review": "Marquer comme lu",
|
|
1477
1478
|
"followup_pending": "Marquer comme lu",
|
|
1478
1479
|
"initiative": "Marquer comme lu",
|
|
1479
|
-
"markRead": "Marquer comme lu"
|
|
1480
|
+
"markRead": "Marquer comme lu",
|
|
1481
|
+
"fork_decision_pending": "Marquer comme lu"
|
|
1480
1482
|
},
|
|
1481
1483
|
"toast": {
|
|
1482
1484
|
"acted": "Marqué comme traité",
|
|
@@ -2031,6 +2033,18 @@
|
|
|
2031
2033
|
"confirmDelete": {
|
|
2032
2034
|
"title": "Supprimer cette politique de risque ?",
|
|
2033
2035
|
"body": "\"{name}\" sera supprimé. Les tâches qui l'utilisent reviendront à la politique par défaut de l'espace de travail."
|
|
2036
|
+
},
|
|
2037
|
+
"forkDecision": {
|
|
2038
|
+
"label": "Décision de bifurcation d'implémentation",
|
|
2039
|
+
"hint": "En mode auto, proposer des approches foncièrement différentes (et faire une pause pour choisir) quand l'estimation de la tâche atteint un seuil.",
|
|
2040
|
+
"minComplexity": "Complexité min.",
|
|
2041
|
+
"minRisk": "Risque min.",
|
|
2042
|
+
"minImpact": "Impact min.",
|
|
2043
|
+
"onMissingLabel": "Aucune estimation",
|
|
2044
|
+
"onMissing": {
|
|
2045
|
+
"run": "Proposer quand même",
|
|
2046
|
+
"skip": "Ignorer"
|
|
2047
|
+
}
|
|
2034
2048
|
}
|
|
2035
2049
|
},
|
|
2036
2050
|
"observabilityConnection": {
|
|
@@ -3204,7 +3218,11 @@
|
|
|
3204
3218
|
"subtasksInProgress": "· {count} en cours",
|
|
3205
3219
|
"clickToRead": "Cliquez pour lire la sortie de cet agent",
|
|
3206
3220
|
"reviewApprove": "Revoir et approuver la proposition de {agent}",
|
|
3207
|
-
"resolve": "Résoudre : {question}"
|
|
3221
|
+
"resolve": "Résoudre : {question}",
|
|
3222
|
+
"forkDecision": {
|
|
3223
|
+
"proposing": "Proposition d'approches…",
|
|
3224
|
+
"choose": "Choisir une approche"
|
|
3225
|
+
}
|
|
3208
3226
|
},
|
|
3209
3227
|
"health": {
|
|
3210
3228
|
"title": "État des pipelines",
|
|
@@ -4539,5 +4557,34 @@
|
|
|
4539
4557
|
"hint": "Provisionnez éventuellement cet environnement maintenant pour tester la recette.",
|
|
4540
4558
|
"run": "Provisionnement d'essai"
|
|
4541
4559
|
}
|
|
4560
|
+
},
|
|
4561
|
+
"forkDecision": {
|
|
4562
|
+
"title": "Choisir une approche d'implémentation",
|
|
4563
|
+
"titleWithBlock": "Choisir une approche pour {title}",
|
|
4564
|
+
"subtitle": "Des façons foncièrement différentes de réaliser cette tâche, avant d'écrire du code.",
|
|
4565
|
+
"seam": "Où cela intervient :",
|
|
4566
|
+
"recommended": "Recommandé",
|
|
4567
|
+
"riskNotes": "Risque :",
|
|
4568
|
+
"noteLabel": "Note d'orientation (facultatif)",
|
|
4569
|
+
"notePlaceholder": "Ce que l'implémentation doit garder à l'esprit…",
|
|
4570
|
+
"choose": "Utiliser cette approche",
|
|
4571
|
+
"proposing": {
|
|
4572
|
+
"title": "Recherche des approches…",
|
|
4573
|
+
"hint": "Lecture du code pour trouver les façons foncièrement différentes de le construire."
|
|
4574
|
+
},
|
|
4575
|
+
"singlePath": {
|
|
4576
|
+
"title": "Une approche évidente"
|
|
4577
|
+
},
|
|
4578
|
+
"chosen": {
|
|
4579
|
+
"title": "Approche choisie",
|
|
4580
|
+
"note": "Note : {note}"
|
|
4581
|
+
},
|
|
4582
|
+
"custom": {
|
|
4583
|
+
"title": "Saisir votre propre approche",
|
|
4584
|
+
"placeholder": "Décrivez comment vous voulez que ce soit implémenté…"
|
|
4585
|
+
},
|
|
4586
|
+
"empty": {
|
|
4587
|
+
"title": "Rien à décider"
|
|
4588
|
+
}
|
|
4542
4589
|
}
|
|
4543
4590
|
}
|