@cat-factory/app 0.280.1 → 0.280.2
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/binaryCandidates/BinaryCandidatesWindow.vue +142 -5
- package/app/components/board/AddTaskModal.vue +9 -0
- package/app/components/board/ReviewFrictionDialog.vue +35 -4
- package/app/components/brainstorm/BrainstormWindow.vue +19 -1
- package/app/components/common/ConfirmDialog.vue +26 -0
- package/app/components/docs/DocInterviewWindow.vue +40 -38
- package/app/components/followUp/FollowUpWindow.vue +32 -2
- package/app/components/forkDecision/ForkDecisionWindow.vue +44 -5
- package/app/components/gates/GateResultView.vue +14 -1
- package/app/components/humanTest/HumanTestWindow.vue +13 -1
- package/app/components/initiative/InitiativePlanDecision.vue +16 -3
- package/app/components/initiative/InitiativePlanReview.vue +16 -1
- package/app/components/initiative/InitiativePlanningWindow.vue +50 -52
- package/app/components/initiative/InitiativeTrackerWindow.vue +59 -1
- package/app/components/judge/JudgeResultView.vue +14 -1
- package/app/components/panels/ResultWindowDrafts.logic.spec.ts +233 -0
- package/app/components/panels/inspector/ServiceTestSecrets.vue +17 -6
- package/app/components/pipeline/PipelineHealthModal.vue +55 -16
- package/app/components/prReview/PrReviewWindow.vue +23 -4
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +19 -1
- package/app/composables/useConfirm.spec.ts +62 -0
- package/app/composables/useConfirm.ts +6 -1
- package/app/composables/useInterviewDrafts.spec.ts +198 -0
- package/app/composables/useInterviewDrafts.ts +184 -0
- package/app/stores/binaryCandidates.ts +26 -3
- package/app/stores/ui/modals.ts +11 -0
- package/app/utils/binaryCandidates.spec.ts +45 -1
- package/app/utils/binaryCandidates.ts +33 -0
- package/i18n/locales/de.json +20 -2
- package/i18n/locales/en.json +20 -2
- package/i18n/locales/es.json +20 -2
- package/i18n/locales/fr.json +20 -2
- package/i18n/locales/he.json +20 -2
- package/i18n/locales/it.json +20 -2
- package/i18n/locales/ja.json +20 -2
- package/i18n/locales/pl.json +20 -2
- package/i18n/locales/tr.json +20 -2
- package/i18n/locales/uk.json +20 -2
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { computed, reactive, watch } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-question answer drafts for an INTERVIEW gate window (the initiative planner's interviewer and
|
|
5
|
+
* the doc-authoring interviewer), plus the two ways they leave the browser: one answer on blur, and
|
|
6
|
+
* every dirty answer on the way out.
|
|
7
|
+
*
|
|
8
|
+
* Both windows hold the same shape of draft for the same reason, and recording one is a PLAIN SAVE:
|
|
9
|
+
* it writes the reply without resolving the interview, which is what the window's own two commands
|
|
10
|
+
* do. That is what makes the FLUSH disposition correct here rather than a discard prompt (see
|
|
11
|
+
* `ResultWindowDrafts.logic.spec.ts` for the rule and the per-window table).
|
|
12
|
+
*
|
|
13
|
+
* It is ONE seam because the two windows held byte-identical copies of this logic and only ever got
|
|
14
|
+
* fixed one at a time: the doc interview grew a close-time flush while the planner kept dropping
|
|
15
|
+
* answers on close, and neither reported a failed write. Three properties are easy to lose when this
|
|
16
|
+
* is hand-rolled per window, and each one was:
|
|
17
|
+
*
|
|
18
|
+
* - The flush CAPTURES what it needs synchronously. `blockId` and everything derived from it go
|
|
19
|
+
* null the instant the view tears down, so an awaited loop that re-reads them writes nowhere.
|
|
20
|
+
* - Each answer settles INDEPENDENTLY. A loop that awaited straight through dropped every answer
|
|
21
|
+
* after the first rejection, with the window already gone.
|
|
22
|
+
* - Every path REPORTS its own failure. Both backing stores rethrow and Vue discards a handler's
|
|
23
|
+
* returned promise, so an unreported write is an unhandled rejection and, to the user, a no-op.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** The minimum an exchange has to carry for a draft answer to be held against it. */
|
|
27
|
+
export interface InterviewDraftQuestion {
|
|
28
|
+
/**
|
|
29
|
+
* The id the answer write addresses. Optional because the wire shape leaves it optional (a
|
|
30
|
+
* hand-authored or fixture exchange parses without one); see {@link useInterviewDrafts}'s
|
|
31
|
+
* `addressable`.
|
|
32
|
+
*/
|
|
33
|
+
id?: string
|
|
34
|
+
/** Stable key for the list and the draft map: the question id, or its index as a fallback. */
|
|
35
|
+
key: string
|
|
36
|
+
/** What is already recorded. Seeds the draft, and decides whether the draft is dirty. */
|
|
37
|
+
answer?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useInterviewDrafts<Q extends InterviewDraftQuestion>(opts: {
|
|
41
|
+
/** The block the answers are written against, or null while no view is open. */
|
|
42
|
+
blockId: () => string | null
|
|
43
|
+
/** Every exchange the session holds, settled rounds included. */
|
|
44
|
+
questions: () => Q[]
|
|
45
|
+
/** The subset still owing an answer, which is what the submit button waits on. */
|
|
46
|
+
pending: () => Q[]
|
|
47
|
+
/** Record ONE answer against the block. */
|
|
48
|
+
write: (blockId: string, questionId: string, answer: string) => Promise<unknown>
|
|
49
|
+
/**
|
|
50
|
+
* Whether this question's draft may be written at all. A question set aside as not-relevant had
|
|
51
|
+
* its recorded answer cleared, so writing a stale local draft back would silently re-answer it.
|
|
52
|
+
*/
|
|
53
|
+
writable?: (question: Q) => boolean
|
|
54
|
+
/** Toast titles for a failed write: one answer lost, and several (which takes a `{ count }`). */
|
|
55
|
+
failureTitleKeys: { one: string; many: string }
|
|
56
|
+
}) {
|
|
57
|
+
const { present } = usePipelineErrorToast()
|
|
58
|
+
|
|
59
|
+
const drafts = reactive<Record<string, string>>({})
|
|
60
|
+
// Seeded from the entity and refreshed as new rounds arrive, without clobbering an answer the
|
|
61
|
+
// human is mid-edit on.
|
|
62
|
+
watch(
|
|
63
|
+
opts.questions,
|
|
64
|
+
(list) => {
|
|
65
|
+
for (const question of list) {
|
|
66
|
+
if (!(question.key in drafts)) drafts[question.key] = question.answer ?? ''
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{ immediate: true },
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Whether an answer to this exchange can be RECORDED at all: the write addresses a question BY
|
|
74
|
+
* ID, so one without an id has nowhere for an answer to go.
|
|
75
|
+
*
|
|
76
|
+
* Callers disable that question's input and say why. Accepting text into it instead would take an
|
|
77
|
+
* answer the flush could only drop, which is the silent loss this seam exists to end.
|
|
78
|
+
*/
|
|
79
|
+
function addressable(question: Q): boolean {
|
|
80
|
+
return typeof question.id === 'string' && question.id.length > 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Questions still missing a drafted answer, which is what a submit button gates on (and renders,
|
|
85
|
+
* because a disabled button with no stated reason is itself a "nothing happened").
|
|
86
|
+
*
|
|
87
|
+
* Unaddressable questions are excluded: nothing the human can type clears one, so counting it
|
|
88
|
+
* would disable the submit for good.
|
|
89
|
+
*/
|
|
90
|
+
const unanswered = computed(
|
|
91
|
+
() => opts.pending().filter((q) => addressable(q) && !drafts[q.key]?.trim()).length,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Persist one answer when its draft differs from what is recorded. The block id is threaded in
|
|
96
|
+
* rather than read off `opts.blockId()`, so a flush that started as the window closed still writes
|
|
97
|
+
* to the right board.
|
|
98
|
+
*/
|
|
99
|
+
async function persist(blockId: string, question: Q): Promise<void> {
|
|
100
|
+
const id = question.id
|
|
101
|
+
if (!id || opts.writable?.(question) === false) return
|
|
102
|
+
const next = (drafts[question.key] ?? '').trim()
|
|
103
|
+
if (!next || next === (question.answer ?? '').trim()) return
|
|
104
|
+
await opts.write(blockId, id, next)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Report a failed write, naming how many answers did not make it. */
|
|
108
|
+
function report(failed: number, cause: unknown): void {
|
|
109
|
+
present(cause, failed === 1 ? opts.failureTitleKeys.one : opts.failureTitleKeys.many, {
|
|
110
|
+
count: failed,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Persist every dirty draft, each on its OWN: one rejection may not cost the answers after it.
|
|
116
|
+
* Returns how many could not be written plus the first cause, which is what the caller reports.
|
|
117
|
+
*
|
|
118
|
+
* Sequential rather than concurrent because each write is a read-modify-write of the session's one
|
|
119
|
+
* question-and-answer array, so two in flight would race to overwrite each other's answer.
|
|
120
|
+
*/
|
|
121
|
+
async function flushAll(blockId: string, list: Q[]): Promise<{ failed: number; cause: unknown }> {
|
|
122
|
+
let failed = 0
|
|
123
|
+
let cause: unknown
|
|
124
|
+
for (const question of list) {
|
|
125
|
+
try {
|
|
126
|
+
await persist(blockId, question)
|
|
127
|
+
} catch (error) {
|
|
128
|
+
failed += 1
|
|
129
|
+
if (failed === 1) cause = error
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { failed, cause }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Save this ONE answer against the block that is open right now: the blur handler, and the same
|
|
137
|
+
* call an adopted recommendation makes. Detached, because Vue discards a handler's returned
|
|
138
|
+
* promise, so the failure is reported here or nowhere.
|
|
139
|
+
*/
|
|
140
|
+
function saveAnswer(question: Q): void {
|
|
141
|
+
const blockId = opts.blockId()
|
|
142
|
+
if (!blockId) return
|
|
143
|
+
void persist(blockId, question).catch((error) => report(1, error))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Persist every dirty draft on the way out, detached so it can be called from the synchronous
|
|
148
|
+
* close hook. A close-time flush is the one path with no button left on screen to have reported a
|
|
149
|
+
* failure, so it reports the loss itself.
|
|
150
|
+
*/
|
|
151
|
+
function flushDrafts(): void {
|
|
152
|
+
const blockId = opts.blockId()
|
|
153
|
+
if (!blockId) return
|
|
154
|
+
const list = [...opts.questions()]
|
|
155
|
+
void (async () => {
|
|
156
|
+
const { failed, cause } = await flushAll(blockId, list)
|
|
157
|
+
if (failed > 0) report(failed, cause)
|
|
158
|
+
})()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Flush every dirty draft, then run a window action, but ONLY if every draft was written: an
|
|
163
|
+
* answer that failed to save may not be submitted as if it were there. On a failure the report is
|
|
164
|
+
* the whole outcome, and the window is left as it is, with the same button on screen and the text
|
|
165
|
+
* still in its box.
|
|
166
|
+
*/
|
|
167
|
+
async function flushThen(
|
|
168
|
+
action: (blockId: string) => Promise<unknown>,
|
|
169
|
+
failureTitleKey: string,
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
const blockId = opts.blockId()
|
|
172
|
+
if (!blockId) return
|
|
173
|
+
const { failed, cause } = await flushAll(blockId, [...opts.questions()])
|
|
174
|
+
if (failed > 0) {
|
|
175
|
+
report(failed, cause)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
// The action rejects too (both backing stores rethrow) and it is reached from a click handler
|
|
179
|
+
// whose promise Vue discards, so its failure is reported here or nowhere.
|
|
180
|
+
await action(blockId).catch((error) => present(error, failureTitleKey))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { drafts, addressable, unanswered, saveAnswer, flushDrafts, flushThen }
|
|
184
|
+
}
|
|
@@ -21,6 +21,12 @@ export const useBinaryCandidatesStore = defineStore('binaryCandidates', () => {
|
|
|
21
21
|
|
|
22
22
|
/** True while a keep call is in flight (drives the button spinner / disabled state). */
|
|
23
23
|
const keeping = ref(false)
|
|
24
|
+
/**
|
|
25
|
+
* True while the warm-up read is in flight. The window renders no state until it settles, and
|
|
26
|
+
* "still fetching" must not render as the "nothing to choose between" empty state: on this
|
|
27
|
+
* surface that empty state is a claim the run generated nothing to compare.
|
|
28
|
+
*/
|
|
29
|
+
const loading = ref(false)
|
|
24
30
|
/** The last error message from an action, surfaced inline; cleared on the next action. */
|
|
25
31
|
const error = ref<string | null>(null)
|
|
26
32
|
|
|
@@ -48,9 +54,24 @@ export const useBinaryCandidatesStore = defineStore('binaryCandidates', () => {
|
|
|
48
54
|
if (step) step.binaryCandidates = state
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The most recent {@link load} attempt. Only the latest one may write `loading` or `error`: two
|
|
59
|
+
* reads do overlap (a Retry beside a window switching run, an open triggered while one is in
|
|
60
|
+
* flight), and a superseded attempt settling afterwards used to clear the spinner and stamp its
|
|
61
|
+
* own verdict over the newer read's. A slow failure landing after a fast success is the bad case,
|
|
62
|
+
* because it reports a load failure across candidates that are on screen.
|
|
63
|
+
*/
|
|
64
|
+
let attempt = 0
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Warm the live state from the GET (the stream also keeps it fresh). The failure is RECORDED
|
|
68
|
+
* rather than swallowed: with no state on the step, a failed read and a run that produced no
|
|
69
|
+
* candidates are the same `null` and opposite facts, and only one of them is worth a Retry.
|
|
70
|
+
*/
|
|
52
71
|
async function load(executionId: string): Promise<void> {
|
|
72
|
+
const mine = ++attempt
|
|
53
73
|
error.value = null
|
|
74
|
+
loading.value = true
|
|
54
75
|
try {
|
|
55
76
|
await execution.echoAfter(
|
|
56
77
|
executionId,
|
|
@@ -60,7 +81,9 @@ export const useBinaryCandidatesStore = defineStore('binaryCandidates', () => {
|
|
|
60
81
|
},
|
|
61
82
|
)
|
|
62
83
|
} catch (e) {
|
|
63
|
-
error.value = e instanceof Error ? e.message : 'Failed to load'
|
|
84
|
+
if (mine === attempt) error.value = e instanceof Error ? e.message : 'Failed to load'
|
|
85
|
+
} finally {
|
|
86
|
+
if (mine === attempt) loading.value = false
|
|
64
87
|
}
|
|
65
88
|
}
|
|
66
89
|
|
|
@@ -85,5 +108,5 @@ export const useBinaryCandidatesStore = defineStore('binaryCandidates', () => {
|
|
|
85
108
|
}
|
|
86
109
|
}
|
|
87
110
|
|
|
88
|
-
return { keeping, error, load, keep }
|
|
111
|
+
return { keeping, loading, error, load, keep }
|
|
89
112
|
})
|
package/app/stores/ui/modals.ts
CHANGED
|
@@ -74,6 +74,17 @@ export interface ReviewFrictionModalContext {
|
|
|
74
74
|
debt: ReviewDebtRow[]
|
|
75
75
|
/** Retry the create with `acknowledgeReviewDebt` — present only for the soft `warn` tier. */
|
|
76
76
|
onConfirm: (() => void) | null
|
|
77
|
+
/**
|
|
78
|
+
* Whether the opener's create is in flight right now, so "Create anyway" can show its spinner and
|
|
79
|
+
* refuse a second click (UX-78). A GETTER over the opener's own `saving` ref rather than a copied
|
|
80
|
+
* boolean: the dialog's context object is captured once at open, so a snapshot would be frozen at
|
|
81
|
+
* `false` for the whole retry. Reading it inside a `computed` keeps the reactivity.
|
|
82
|
+
*
|
|
83
|
+
* Load-bearing, not cosmetic: the retry's first await resolves the staged context attachments
|
|
84
|
+
* (a network round-trip per item), so a second click during that window filed a SECOND task and
|
|
85
|
+
* started a second pipeline run against the same request.
|
|
86
|
+
*/
|
|
87
|
+
pending?: () => boolean
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
/** Clears both hub came-from markers; injected into the slices whose `open*` handlers reset them. */
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import type { PipelineStep } from '~/types/execution'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
binaryCandidateAbsence,
|
|
5
|
+
binaryCandidateHasWarnings,
|
|
6
|
+
binaryCandidateView,
|
|
7
|
+
} from './binaryCandidates'
|
|
4
8
|
|
|
5
9
|
function step(overrides: Record<string, unknown> = {}): PipelineStep {
|
|
6
10
|
// The candidate override MERGES into the base state rather than replacing it, so a case that
|
|
@@ -108,3 +112,43 @@ describe('binaryCandidateHasWarnings', () => {
|
|
|
108
112
|
}
|
|
109
113
|
})
|
|
110
114
|
})
|
|
115
|
+
|
|
116
|
+
describe('binaryCandidateAbsence', () => {
|
|
117
|
+
const RUN = 'exec_1'
|
|
118
|
+
|
|
119
|
+
// The window used to render a titled shell with a blank body for all four of these (UX-80).
|
|
120
|
+
// They are different facts, and only one of them is a claim about the RUN.
|
|
121
|
+
it('separates no run, a read in flight, a failed read, and a run that compared nothing', () => {
|
|
122
|
+
expect(binaryCandidateAbsence(false, null, null)).toBe('no_run')
|
|
123
|
+
expect(binaryCandidateAbsence(true, null, RUN)).toBe('loading')
|
|
124
|
+
expect(binaryCandidateAbsence(false, 'network down', RUN)).toBe('load_failed')
|
|
125
|
+
expect(binaryCandidateAbsence(false, null, RUN)).toBe('nothing_compared')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// A Retry re-enters `loading` while the PREVIOUS attempt's message is still recorded. Reporting
|
|
129
|
+
// the stale failure over the live attempt would make the button look like it did nothing.
|
|
130
|
+
it('lets a fresh attempt outrank the error it is clearing', () => {
|
|
131
|
+
expect(binaryCandidateAbsence(true, 'network down', RUN)).toBe('loading')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// 'nothing_compared' renders "nothing to compare", which is a statement about what the run
|
|
135
|
+
// produced. A request that never landed knows nothing about that, so it must never reach here.
|
|
136
|
+
it('never claims emptiness on the strength of a request that failed', () => {
|
|
137
|
+
for (const error of ['boom', 'Failed to load']) {
|
|
138
|
+
expect(binaryCandidateAbsence(false, error, RUN)).not.toBe('nothing_compared')
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
// With no run the warm-up read is never started, so the store's flags belong to some OTHER
|
|
143
|
+
// window's read. Reporting them here would describe a run nobody asked about, and falling through
|
|
144
|
+
// to emptiness would claim this run compared nothing without ever having looked.
|
|
145
|
+
it('never reports a state it did not read, nor emptiness, when there is no run', () => {
|
|
146
|
+
for (const loading of [true, false]) {
|
|
147
|
+
for (const error of [null, 'network down']) {
|
|
148
|
+
for (const run of [null, undefined, '']) {
|
|
149
|
+
expect(binaryCandidateAbsence(loading, error, run)).toBe('no_run')
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
})
|
|
@@ -127,6 +127,39 @@ export function binaryCandidateHasWarnings(view: BinaryCandidateView): boolean {
|
|
|
127
127
|
return invalidEntries > 0 || omitted > 0 || unusablePreviews > 0
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Why the window has no comparison to render. Four different facts that a single blank body
|
|
132
|
+
* conflated (UX-80), each needing a different reaction from the reader: nothing to read from, wait,
|
|
133
|
+
* retry, or accept that the run compared nothing. `nothing_compared` is a CLAIM about the run, so
|
|
134
|
+
* it is only ever the answer once a read of that run has settled and not failed.
|
|
135
|
+
*/
|
|
136
|
+
export type BinaryCandidateAbsence = 'no_run' | 'loading' | 'load_failed' | 'nothing_compared'
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The absence to render when {@link binaryCandidateView} produced nothing.
|
|
140
|
+
*
|
|
141
|
+
* Precedence is the point, and it is stated here rather than in the template's branch order:
|
|
142
|
+
*
|
|
143
|
+
* - No RUN outranks everything. The window is keyed to an execution and the warm-up read is
|
|
144
|
+
* skipped without one, so `loading`/`error` can only be describing some other window's read;
|
|
145
|
+
* reporting either would attribute a state to a run nobody asked about, and reporting emptiness
|
|
146
|
+
* would claim this run compared nothing on the strength of never having looked.
|
|
147
|
+
* - An in-flight read outranks a stale error from the previous attempt, so a Retry does not keep
|
|
148
|
+
* showing the failure it is busy clearing.
|
|
149
|
+
* - A recorded error outranks emptiness, so a request that never landed cannot render as "this run
|
|
150
|
+
* generated nothing to compare".
|
|
151
|
+
*/
|
|
152
|
+
export function binaryCandidateAbsence(
|
|
153
|
+
loading: boolean,
|
|
154
|
+
error: string | null | undefined,
|
|
155
|
+
executionId: string | null | undefined,
|
|
156
|
+
): BinaryCandidateAbsence {
|
|
157
|
+
if (!executionId) return 'no_run'
|
|
158
|
+
if (loading) return 'loading'
|
|
159
|
+
if (error) return 'load_failed'
|
|
160
|
+
return 'nothing_compared'
|
|
161
|
+
}
|
|
162
|
+
|
|
130
163
|
/**
|
|
131
164
|
* State → the i18n key for the line explaining why no choice was offered.
|
|
132
165
|
*
|
package/i18n/locales/de.json
CHANGED
|
@@ -5689,6 +5689,11 @@
|
|
|
5689
5689
|
"preparingHint": "Die Planung liest zuerst das Repository, damit Sie nicht nach dem gefragt werden, was der Code beantworten kann. Fragen erscheinen hier, sobald das erledigt ist.",
|
|
5690
5690
|
"failed": "Der Planungslauf wurde abgebrochen",
|
|
5691
5691
|
"failedHint": "Er endete, bevor der Planer antworten konnte. Ihre Antworten sind gespeichert; führen Sie die Planung von der Initiative aus erneut aus.",
|
|
5692
|
+
"saveFailed": "Antwort konnte nicht gespeichert werden",
|
|
5693
|
+
"saveFailedCount": "{count} Ihrer Antworten konnten nicht gespeichert werden",
|
|
5694
|
+
"continueFailed": "Antworten konnten nicht gesendet werden",
|
|
5695
|
+
"proceedFailed": "Die Planung konnte nicht gestartet werden",
|
|
5696
|
+
"unanswerable": "Diese Frage hat keine ID, daher kann keine Antwort dazu gespeichert werden.",
|
|
5692
5697
|
"answerPlaceholder": "Ihre Antwort",
|
|
5693
5698
|
"hint": "Antworten senden lässt den Planer Rückfragen stellen; Jetzt planen entwirft den Plan mit den bisherigen Antworten.",
|
|
5694
5699
|
"unanswered": "Unbeantwortete Fragen: {count}",
|
|
@@ -6194,7 +6199,12 @@
|
|
|
6194
6199
|
"status": {
|
|
6195
6200
|
"awaiting": "Warten auf Antworten",
|
|
6196
6201
|
"done": "Fertig"
|
|
6197
|
-
}
|
|
6202
|
+
},
|
|
6203
|
+
"saveFailedCount": "{count} Ihrer Antworten konnten nicht gespeichert werden",
|
|
6204
|
+
"continueFailed": "Antworten konnten nicht gesendet werden",
|
|
6205
|
+
"proceedFailed": "Der Entwurf konnte nicht gestartet werden",
|
|
6206
|
+
"unanswerable": "Diese Frage hat keine ID, daher kann keine Antwort dazu gespeichert werden.",
|
|
6207
|
+
"saveFailed": "Antwort konnte nicht gespeichert werden"
|
|
6198
6208
|
},
|
|
6199
6209
|
"gates": {
|
|
6200
6210
|
"subtitle": {
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "Der Schritt hat seine Kandidaten nie deklariert, es gab also nichts zu vergleichen. Der Lauf ging weiter.",
|
|
8036
8046
|
"parseFailed": "Die Kandidaten-Deklaration des Schritts war nicht lesbar, es gab also nichts zu vergleichen. Der Lauf ging weiter.",
|
|
8037
8047
|
"noCandidates": "Der Schritt hat keine Kandidaten bereitgestellt, es gab also nichts zu vergleichen. Der Lauf ging weiter."
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "Kein Lauf zum Auslesen",
|
|
8051
|
+
"hint": "Dieser Vergleich ist an einen Pipeline-Lauf gebunden. Öffnen Sie ihn über die Karte oder die Zeitleiste des erzeugenden Schritts, damit die Kandidaten geladen werden können."
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "Nichts zu vergleichen"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "Die erzeugten Kandidaten konnten nicht geladen werden."
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|
package/i18n/locales/en.json
CHANGED
|
@@ -4768,7 +4768,12 @@
|
|
|
4768
4768
|
"status": {
|
|
4769
4769
|
"awaiting": "Awaiting answers",
|
|
4770
4770
|
"done": "Done"
|
|
4771
|
-
}
|
|
4771
|
+
},
|
|
4772
|
+
"saveFailedCount": "Could not save {count} of your answers",
|
|
4773
|
+
"continueFailed": "Could not submit your answers",
|
|
4774
|
+
"proceedFailed": "Could not start the draft",
|
|
4775
|
+
"unanswerable": "This question carries no id, so an answer cannot be recorded against it.",
|
|
4776
|
+
"saveFailed": "Could not save your answer"
|
|
4772
4777
|
},
|
|
4773
4778
|
"documents": {
|
|
4774
4779
|
"picker": {
|
|
@@ -7345,6 +7350,11 @@
|
|
|
7345
7350
|
"preparingHint": "Planning reads the repository first, so you are not asked what the code can answer. Questions appear here once that is done.",
|
|
7346
7351
|
"failed": "The planning run stopped",
|
|
7347
7352
|
"failedHint": "It ended before the planner could answer. Your answers are saved; re-run planning from the initiative to try again.",
|
|
7353
|
+
"saveFailed": "Could not save your answer",
|
|
7354
|
+
"saveFailedCount": "Could not save {count} of your answers",
|
|
7355
|
+
"continueFailed": "Could not submit your answers",
|
|
7356
|
+
"proceedFailed": "Could not start planning",
|
|
7357
|
+
"unanswerable": "This question carries no id, so an answer cannot be recorded against it.",
|
|
7348
7358
|
"answerPlaceholder": "Your answer",
|
|
7349
7359
|
"hint": "Submit answers lets the planner ask follow-ups; Plan now drafts the plan with the answers so far.",
|
|
7350
7360
|
"unanswered": "Unanswered questions: {count}",
|
|
@@ -8324,6 +8334,14 @@
|
|
|
8324
8334
|
"undeclared": "The step never declared its candidates, so there was nothing to compare. The run continued.",
|
|
8325
8335
|
"parseFailed": "The step’s candidate declaration could not be read, so there was nothing to compare. The run continued.",
|
|
8326
8336
|
"noCandidates": "The step staged no candidates, so there was nothing to compare. The run continued."
|
|
8327
|
-
}
|
|
8337
|
+
},
|
|
8338
|
+
"noRun": {
|
|
8339
|
+
"title": "No run to read",
|
|
8340
|
+
"hint": "This comparison is keyed to a pipeline run. Open it from the generating step's card or timeline so its candidates can be loaded."
|
|
8341
|
+
},
|
|
8342
|
+
"empty": {
|
|
8343
|
+
"title": "Nothing to compare"
|
|
8344
|
+
},
|
|
8345
|
+
"loadFailed": "Could not load the generated candidates."
|
|
8328
8346
|
}
|
|
8329
8347
|
}
|
package/i18n/locales/es.json
CHANGED
|
@@ -4607,7 +4607,12 @@
|
|
|
4607
4607
|
"status": {
|
|
4608
4608
|
"awaiting": "Esperando respuestas",
|
|
4609
4609
|
"done": "Hecho"
|
|
4610
|
-
}
|
|
4610
|
+
},
|
|
4611
|
+
"saveFailedCount": "No se pudieron guardar {count} de tus respuestas",
|
|
4612
|
+
"continueFailed": "No se pudieron enviar tus respuestas",
|
|
4613
|
+
"proceedFailed": "No se pudo iniciar el borrador",
|
|
4614
|
+
"unanswerable": "Esta pregunta no tiene id, así que no se puede registrar ninguna respuesta para ella.",
|
|
4615
|
+
"saveFailed": "No se pudo guardar tu respuesta"
|
|
4611
4616
|
},
|
|
4612
4617
|
"documents": {
|
|
4613
4618
|
"picker": {
|
|
@@ -7086,6 +7091,11 @@
|
|
|
7086
7091
|
"preparingHint": "La planificacion lee primero el repositorio para no preguntarte lo que el codigo ya responde. Las preguntas apareceran aqui cuando termine.",
|
|
7087
7092
|
"failed": "La ejecucion de planificacion se detuvo",
|
|
7088
7093
|
"failedHint": "Termino antes de que el planificador pudiera responder. Tus respuestas estan guardadas; vuelve a ejecutar la planificacion desde la iniciativa.",
|
|
7094
|
+
"saveFailed": "No se pudo guardar tu respuesta",
|
|
7095
|
+
"saveFailedCount": "No se pudieron guardar {count} de tus respuestas",
|
|
7096
|
+
"continueFailed": "No se pudieron enviar tus respuestas",
|
|
7097
|
+
"proceedFailed": "No se pudo iniciar la planificación",
|
|
7098
|
+
"unanswerable": "Esta pregunta no tiene id, así que no se puede registrar ninguna respuesta para ella.",
|
|
7089
7099
|
"answerPlaceholder": "Tu respuesta",
|
|
7090
7100
|
"hint": "Enviar respuestas permite al planificador hacer mas preguntas; Planificar ahora redacta el plan con las respuestas actuales.",
|
|
7091
7101
|
"unanswered": "Preguntas sin responder: {count}",
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "El paso nunca declaró sus candidatos, así que no había nada que comparar. La ejecución continuó.",
|
|
8036
8046
|
"parseFailed": "No se pudo leer la declaración de candidatos del paso, así que no había nada que comparar. La ejecución continuó.",
|
|
8037
8047
|
"noCandidates": "El paso no preparó ningún candidato, así que no había nada que comparar. La ejecución continuó."
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "No hay ninguna ejecución que leer",
|
|
8051
|
+
"hint": "Esta comparación está vinculada a una ejecución de la canalización. Ábrela desde la tarjeta o la cronología del paso que la generó para que se puedan cargar sus candidatos."
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "Nada que comparar"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "No se pudieron cargar los candidatos generados."
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4607,7 +4607,12 @@
|
|
|
4607
4607
|
"status": {
|
|
4608
4608
|
"awaiting": "En attente de réponses",
|
|
4609
4609
|
"done": "Terminé"
|
|
4610
|
-
}
|
|
4610
|
+
},
|
|
4611
|
+
"saveFailedCount": "Impossible d’enregistrer {count} de vos réponses",
|
|
4612
|
+
"continueFailed": "Impossible d’envoyer vos réponses",
|
|
4613
|
+
"proceedFailed": "Impossible de lancer la rédaction",
|
|
4614
|
+
"unanswerable": "Cette question n’a pas d’identifiant : aucune réponse ne peut y être enregistrée.",
|
|
4615
|
+
"saveFailed": "Impossible d’enregistrer votre réponse"
|
|
4611
4616
|
},
|
|
4612
4617
|
"documents": {
|
|
4613
4618
|
"picker": {
|
|
@@ -7086,6 +7091,11 @@
|
|
|
7086
7091
|
"preparingHint": "La planification lit d'abord le depot afin de ne pas vous demander ce que le code peut repondre. Les questions apparaitront ici une fois termine.",
|
|
7087
7092
|
"failed": "L'execution de planification s'est arretee",
|
|
7088
7093
|
"failedHint": "Elle s'est terminee avant que le planificateur puisse repondre. Vos reponses sont enregistrees ; relancez la planification depuis l'initiative.",
|
|
7094
|
+
"saveFailed": "Impossible d’enregistrer votre réponse",
|
|
7095
|
+
"saveFailedCount": "Impossible d’enregistrer {count} de vos réponses",
|
|
7096
|
+
"continueFailed": "Impossible d’envoyer vos réponses",
|
|
7097
|
+
"proceedFailed": "Impossible de lancer la planification",
|
|
7098
|
+
"unanswerable": "Cette question n’a pas d’identifiant : aucune réponse ne peut y être enregistrée.",
|
|
7089
7099
|
"answerPlaceholder": "Votre reponse",
|
|
7090
7100
|
"hint": "Envoyer les reponses permet au planificateur de poser des questions complementaires ; Planifier maintenant redige le plan avec les reponses actuelles.",
|
|
7091
7101
|
"unanswered": "Questions sans reponse : {count}",
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "L’étape n’a jamais déclaré ses candidats, il n’y avait donc rien à comparer. L’exécution a continué.",
|
|
8036
8046
|
"parseFailed": "La déclaration de candidats de l’étape était illisible, il n’y avait donc rien à comparer. L’exécution a continué.",
|
|
8037
8047
|
"noCandidates": "L’étape n’a préparé aucun candidat, il n’y avait donc rien à comparer. L’exécution a continué."
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "Aucune exécution à lire",
|
|
8051
|
+
"hint": "Cette comparaison est liée à une exécution de pipeline. Ouvrez-la depuis la carte ou la chronologie de l’étape qui l’a produite pour que ses candidats puissent être chargés."
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "Rien à comparer"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "Impossible de charger les candidats générés."
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|
package/i18n/locales/he.json
CHANGED
|
@@ -4607,7 +4607,12 @@
|
|
|
4607
4607
|
"status": {
|
|
4608
4608
|
"awaiting": "ממתין לתשובות",
|
|
4609
4609
|
"done": "הושלם"
|
|
4610
|
-
}
|
|
4610
|
+
},
|
|
4611
|
+
"saveFailedCount": "לא ניתן היה לשמור {count} מהתשובות שלך",
|
|
4612
|
+
"continueFailed": "לא ניתן היה לשלוח את התשובות שלך",
|
|
4613
|
+
"proceedFailed": "לא ניתן היה להתחיל את הטיוטה",
|
|
4614
|
+
"unanswerable": "לשאלה הזאת אין מזהה, ולכן לא ניתן לרשום עבורה תשובה.",
|
|
4615
|
+
"saveFailed": "לא ניתן היה לשמור את התשובה שלך"
|
|
4611
4616
|
},
|
|
4612
4617
|
"documents": {
|
|
4613
4618
|
"picker": {
|
|
@@ -7086,6 +7091,11 @@
|
|
|
7086
7091
|
"preparingHint": "התכנון קורא תחילה את המאגר כדי שלא תישאל על מה שהקוד יכול לענות. השאלות יופיעו כאן בסיום.",
|
|
7087
7092
|
"failed": "הרצת התכנון נעצרה",
|
|
7088
7093
|
"failedHint": "היא הסתיימה לפני שהמתכנן הספיק להשיב. התשובות שלך נשמרו; הרץ תכנון מחדש מהיוזמה.",
|
|
7094
|
+
"saveFailed": "לא ניתן היה לשמור את התשובה שלך",
|
|
7095
|
+
"saveFailedCount": "לא ניתן היה לשמור {count} מהתשובות שלך",
|
|
7096
|
+
"continueFailed": "לא ניתן היה לשלוח את התשובות שלך",
|
|
7097
|
+
"proceedFailed": "לא ניתן היה להתחיל את התכנון",
|
|
7098
|
+
"unanswerable": "לשאלה הזאת אין מזהה, ולכן לא ניתן לרשום עבורה תשובה.",
|
|
7089
7099
|
"answerPlaceholder": "התשובה שלך",
|
|
7090
7100
|
"hint": "שליחת תשובות מאפשרת למתכנן לשאול שאלות המשך; תכנן עכשיו מנסח את התוכנית עם התשובות עד כה.",
|
|
7091
7101
|
"unanswered": "שאלות ללא מענה: {count}",
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "השלב מעולם לא הצהיר על מועמדים, ולכן לא היה מה להשוות. הריצה המשיכה.",
|
|
8036
8046
|
"parseFailed": "לא ניתן היה לקרוא את הצהרת המועמדים של השלב, ולכן לא היה מה להשוות. הריצה המשיכה.",
|
|
8037
8047
|
"noCandidates": "השלב לא הכין אף מועמד, ולכן לא היה מה להשוות. הריצה המשיכה."
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "אין הרצה לקריאה",
|
|
8051
|
+
"hint": "ההשוואה הזאת משויכת להרצה של הפייפליין. פתחו אותה מהכרטיס או מציר הזמן של השלב שיצר אותה, כדי שניתן יהיה לטעון את המועמדים."
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "אין מה להשוות"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "לא ניתן היה לטעון את המועמדים שנוצרו."
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|
package/i18n/locales/it.json
CHANGED
|
@@ -5689,6 +5689,11 @@
|
|
|
5689
5689
|
"preparingHint": "La pianificazione legge prima il repository, cosi non ti viene chiesto cio che il codice puo rispondere. Le domande compariranno qui al termine.",
|
|
5690
5690
|
"failed": "L'esecuzione della pianificazione si e interrotta",
|
|
5691
5691
|
"failedHint": "Si e conclusa prima che il pianificatore potesse rispondere. Le tue risposte sono salvate; riesegui la pianificazione dall'iniziativa.",
|
|
5692
|
+
"saveFailed": "Impossibile salvare la tua risposta",
|
|
5693
|
+
"saveFailedCount": "Impossibile salvare {count} delle tue risposte",
|
|
5694
|
+
"continueFailed": "Impossibile inviare le tue risposte",
|
|
5695
|
+
"proceedFailed": "Impossibile avviare la pianificazione",
|
|
5696
|
+
"unanswerable": "Questa domanda non ha un id, quindi non è possibile registrarvi una risposta.",
|
|
5692
5697
|
"answerPlaceholder": "La tua risposta",
|
|
5693
5698
|
"hint": "Invia risposte consente al pianificatore di porre domande di follow-up; Pianifica ora redige il piano con le risposte fornite finora.",
|
|
5694
5699
|
"unanswered": "Domande senza risposta: {count}",
|
|
@@ -6194,7 +6199,12 @@
|
|
|
6194
6199
|
"status": {
|
|
6195
6200
|
"awaiting": "In attesa di risposte",
|
|
6196
6201
|
"done": "Fatto"
|
|
6197
|
-
}
|
|
6202
|
+
},
|
|
6203
|
+
"saveFailedCount": "Impossibile salvare {count} delle tue risposte",
|
|
6204
|
+
"continueFailed": "Impossibile inviare le tue risposte",
|
|
6205
|
+
"proceedFailed": "Impossibile avviare la bozza",
|
|
6206
|
+
"unanswerable": "Questa domanda non ha un id, quindi non è possibile registrarvi una risposta.",
|
|
6207
|
+
"saveFailed": "Impossibile salvare la tua risposta"
|
|
6198
6208
|
},
|
|
6199
6209
|
"gates": {
|
|
6200
6210
|
"subtitle": {
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "Il passo non ha mai dichiarato i suoi candidati, quindi non c’era nulla da confrontare. L’esecuzione è proseguita.",
|
|
8036
8046
|
"parseFailed": "La dichiarazione dei candidati del passo non era leggibile, quindi non c’era nulla da confrontare. L’esecuzione è proseguita.",
|
|
8037
8047
|
"noCandidates": "Il passo non ha preparato alcun candidato, quindi non c’era nulla da confrontare. L’esecuzione è proseguita."
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "Nessuna esecuzione da leggere",
|
|
8051
|
+
"hint": "Questo confronto è legato a un’esecuzione della pipeline. Aprilo dalla scheda o dalla cronologia del passo che l’ha generato, così i suoi candidati possono essere caricati."
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "Nulla da confrontare"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "Impossibile caricare i candidati generati."
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|
package/i18n/locales/ja.json
CHANGED
|
@@ -4607,7 +4607,12 @@
|
|
|
4607
4607
|
"status": {
|
|
4608
4608
|
"awaiting": "回答待ち",
|
|
4609
4609
|
"done": "完了"
|
|
4610
|
-
}
|
|
4610
|
+
},
|
|
4611
|
+
"saveFailedCount": "回答のうち {count} 件を保存できませんでした",
|
|
4612
|
+
"continueFailed": "回答を送信できませんでした",
|
|
4613
|
+
"proceedFailed": "下書きを開始できませんでした",
|
|
4614
|
+
"unanswerable": "この質問には ID がないため、回答を記録できません。",
|
|
4615
|
+
"saveFailed": "回答を保存できませんでした"
|
|
4611
4616
|
},
|
|
4612
4617
|
"documents": {
|
|
4613
4618
|
"picker": {
|
|
@@ -7086,6 +7091,11 @@
|
|
|
7086
7091
|
"preparingHint": "計画はまずリポジトリを読み取るため、コードから分かることは質問されません。完了すると質問がここに表示されます。",
|
|
7087
7092
|
"failed": "計画の実行が停止しました",
|
|
7088
7093
|
"failedHint": "プランナーが応答する前に終了しました。回答は保存されています。イニシアチブから計画を再実行してください。",
|
|
7094
|
+
"saveFailed": "回答を保存できませんでした",
|
|
7095
|
+
"saveFailedCount": "回答のうち {count} 件を保存できませんでした",
|
|
7096
|
+
"continueFailed": "回答を送信できませんでした",
|
|
7097
|
+
"proceedFailed": "プランニングを開始できませんでした",
|
|
7098
|
+
"unanswerable": "この質問には ID がないため、回答を記録できません。",
|
|
7089
7099
|
"answerPlaceholder": "回答",
|
|
7090
7100
|
"hint": "「回答を送信」ではプランナーが追加の質問をします。「今すぐ計画」ではこれまでの回答をもとに計画を作成します。",
|
|
7091
7101
|
"unanswered": "未回答の質問: {count}",
|
|
@@ -8035,6 +8045,14 @@
|
|
|
8035
8045
|
"undeclared": "このステップは候補を申告しなかったため、比較するものがありませんでした。実行は続行されました。",
|
|
8036
8046
|
"parseFailed": "このステップの候補の申告を読み取れなかったため、比較するものがありませんでした。実行は続行されました。",
|
|
8037
8047
|
"noCandidates": "このステップは候補を用意しなかったため、比較するものがありませんでした。実行は続行されました。"
|
|
8038
|
-
}
|
|
8048
|
+
},
|
|
8049
|
+
"noRun": {
|
|
8050
|
+
"title": "読み取る実行がありません",
|
|
8051
|
+
"hint": "この比較はパイプラインの実行に紐づいています。候補を読み込めるように、生成したステップのカードまたはタイムラインから開いてください。"
|
|
8052
|
+
},
|
|
8053
|
+
"empty": {
|
|
8054
|
+
"title": "比較する候補はありません"
|
|
8055
|
+
},
|
|
8056
|
+
"loadFailed": "生成された候補を読み込めませんでした。"
|
|
8039
8057
|
}
|
|
8040
8058
|
}
|