@cat-factory/app 0.154.1 → 0.155.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/panels/ResultWindowShell.vue +74 -3
- package/app/components/panels/StepValidationReport.vue +66 -0
- package/app/components/panels/inspector/ServiceValidationConfig.vue +208 -0
- package/app/composables/api/validationChecks.ts +39 -0
- package/app/composables/useApi.ts +2 -0
- package/app/modular/panels/inspector.logic.spec.ts +3 -0
- package/app/modular/panels/inspector.logic.ts +5 -0
- package/app/modular/panels/inspector.ts +2 -0
- package/app/stores/validationChecks.ts +111 -0
- package/app/types/validationChecks.ts +17 -0
- package/i18n/locales/de.json +29 -0
- package/i18n/locales/en.json +29 -0
- package/i18n/locales/es.json +29 -0
- package/i18n/locales/fr.json +29 -0
- package/i18n/locales/he.json +29 -0
- package/i18n/locales/it.json +29 -0
- package/i18n/locales/ja.json +29 -0
- package/i18n/locales/pl.json +29 -0
- package/i18n/locales/tr.json +29 -0
- package/i18n/locales/uk.json +29 -0
- package/package.json +2 -2
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
//
|
|
21
21
|
// It also owns the one trailing section every step-backed window shows: the agent's effort
|
|
22
22
|
// self-assessment (see the footer block below), so a window never renders it itself.
|
|
23
|
-
import { computed, ref } from 'vue'
|
|
23
|
+
import { computed, ref, watch } from 'vue'
|
|
24
24
|
import { useModalBehavior } from '@modular-vue/core'
|
|
25
25
|
import StepRestartControl from '~/components/panels/StepRestartControl.vue'
|
|
26
26
|
import StepEffortReport from '~/components/panels/StepEffortReport.vue'
|
|
27
|
+
import StepValidationReport from '~/components/panels/StepValidationReport.vue'
|
|
27
28
|
import { effortBand, effortHint } from '~/utils/effort'
|
|
28
29
|
|
|
29
30
|
/** A pipeline step reference — passed by step-result windows to surface the shared
|
|
@@ -85,11 +86,26 @@ const { dialogRef } = useModalBehavior({
|
|
|
85
86
|
// step whose agent wrote no report both resolve to null, and the footer disappears.
|
|
86
87
|
const ui = useUiStore()
|
|
87
88
|
const execution = useExecutionStore()
|
|
88
|
-
const
|
|
89
|
+
const activeStep = computed(() => {
|
|
89
90
|
const view = ui.resultView
|
|
90
91
|
if (!view || view.instanceId === null || view.stepIndex === null) return null
|
|
91
|
-
return execution.getInstance(view.instanceId)?.steps[view.stepIndex]
|
|
92
|
+
return execution.getInstance(view.instanceId)?.steps[view.stepIndex] ?? null
|
|
92
93
|
})
|
|
94
|
+
const effortReport = computed(() => activeStep.value?.effortReport ?? null)
|
|
95
|
+
// The step's PRE-PR VALIDATION report — the second universal trailing section, resolved the same
|
|
96
|
+
// way and for the same reason: every window whose step ran a coding job can show whether the
|
|
97
|
+
// checkout actually passed the service's checks before the PR opened (and, on a red run, exactly
|
|
98
|
+
// what failed). Absent for a service that configured no checks, and the footer disappears.
|
|
99
|
+
const validationReport = computed(() => activeStep.value?.validation ?? null)
|
|
100
|
+
const validationOpen = ref(false)
|
|
101
|
+
// A failing report is the interesting one, so it opens expanded; a green one stays a one-line row.
|
|
102
|
+
watch(
|
|
103
|
+
validationReport,
|
|
104
|
+
(report) => {
|
|
105
|
+
if (report && !report.passed) validationOpen.value = true
|
|
106
|
+
},
|
|
107
|
+
{ immediate: true },
|
|
108
|
+
)
|
|
93
109
|
// Collapsed by default — the windows own the vertical space, and the row already carries the
|
|
94
110
|
// difficulty plus the gist of what held the agent back.
|
|
95
111
|
const effortOpen = ref(false)
|
|
@@ -204,6 +220,61 @@ const panelClass = computed(() => [
|
|
|
204
220
|
<StepEffortReport :report="effortReport" variant="flat" />
|
|
205
221
|
</div>
|
|
206
222
|
</section>
|
|
223
|
+
|
|
224
|
+
<!-- Shared trailing section: the pre-PR validation report (the service's check commands
|
|
225
|
+
run against the checkout BEFORE the PR opened). Collapsed when green, expanded when
|
|
226
|
+
red — a failed checkout is the one an operator opened the window to read. -->
|
|
227
|
+
<section
|
|
228
|
+
v-if="validationReport"
|
|
229
|
+
class="shrink-0 border-t border-slate-800 bg-slate-900/60"
|
|
230
|
+
data-testid="result-window-validation"
|
|
231
|
+
>
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
class="flex w-full items-center gap-2 px-5 py-2 text-start hover:bg-slate-800/40"
|
|
235
|
+
:aria-expanded="validationOpen"
|
|
236
|
+
data-testid="result-window-validation-toggle"
|
|
237
|
+
@click="validationOpen = !validationOpen"
|
|
238
|
+
>
|
|
239
|
+
<UIcon
|
|
240
|
+
:name="validationReport.passed ? 'i-lucide-shield-check' : 'i-lucide-shield-alert'"
|
|
241
|
+
class="h-3.5 w-3.5 shrink-0"
|
|
242
|
+
:class="validationReport.passed ? 'text-emerald-400' : 'text-rose-400'"
|
|
243
|
+
/>
|
|
244
|
+
<span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
245
|
+
{{ t('panels.stepDetail.validation.heading') }}
|
|
246
|
+
</span>
|
|
247
|
+
<span
|
|
248
|
+
class="shrink-0 rounded px-1.5 py-0.5 text-[11px] font-medium tabular-nums"
|
|
249
|
+
:class="
|
|
250
|
+
validationReport.passed
|
|
251
|
+
? 'bg-emerald-500/15 text-emerald-300'
|
|
252
|
+
: 'bg-rose-500/15 text-rose-300'
|
|
253
|
+
"
|
|
254
|
+
>
|
|
255
|
+
{{
|
|
256
|
+
validationReport.passed
|
|
257
|
+
? t('panels.stepDetail.validation.passed')
|
|
258
|
+
: t('panels.stepDetail.validation.failed')
|
|
259
|
+
}}
|
|
260
|
+
</span>
|
|
261
|
+
<span class="min-w-0 flex-1 truncate text-[12px] text-slate-400">
|
|
262
|
+
{{
|
|
263
|
+
t('panels.stepDetail.validation.attempts', {
|
|
264
|
+
attempts: validationReport.attempts,
|
|
265
|
+
maxAttempts: validationReport.maxAttempts,
|
|
266
|
+
})
|
|
267
|
+
}}
|
|
268
|
+
</span>
|
|
269
|
+
<UIcon
|
|
270
|
+
:name="validationOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
|
|
271
|
+
class="ms-auto h-3.5 w-3.5 shrink-0 text-slate-500"
|
|
272
|
+
/>
|
|
273
|
+
</button>
|
|
274
|
+
<div v-if="validationOpen" class="max-h-72 overflow-y-auto px-5 pb-3">
|
|
275
|
+
<StepValidationReport :report="validationReport" />
|
|
276
|
+
</div>
|
|
277
|
+
</section>
|
|
207
278
|
</div>
|
|
208
279
|
</div>
|
|
209
280
|
</Teleport>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { ValidationReport } from '~/types/validationChecks'
|
|
4
|
+
|
|
5
|
+
// The PRE-PR VALIDATION report for a step: which of the service's configured check commands
|
|
6
|
+
// passed against the checkout before the PR was allowed to open, their exit codes, and a
|
|
7
|
+
// bounded, secret-scrubbed tail of each command's output. Produced by the executor-harness
|
|
8
|
+
// running the commands — never a model self-report — so this is the run's captured PROOF on a
|
|
9
|
+
// green run and its EVIDENCE on a failed one. See docs/initiatives/pre-pr-validation.md.
|
|
10
|
+
const props = defineProps<{ report: ValidationReport }>()
|
|
11
|
+
const { t } = useI18n()
|
|
12
|
+
|
|
13
|
+
const failed = computed(() => props.report.outcomes.filter((o) => !o.passed))
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div class="space-y-2" data-testid="step-validation-report">
|
|
18
|
+
<p class="text-[11px] text-slate-400">
|
|
19
|
+
{{
|
|
20
|
+
report.passed
|
|
21
|
+
? t('panels.stepDetail.validation.passedSummary', {
|
|
22
|
+
total: report.outcomes.length,
|
|
23
|
+
attempts: report.attempts,
|
|
24
|
+
})
|
|
25
|
+
: t('panels.stepDetail.validation.failedSummary', {
|
|
26
|
+
failed: failed.length,
|
|
27
|
+
total: report.outcomes.length,
|
|
28
|
+
attempts: report.attempts,
|
|
29
|
+
maxAttempts: report.maxAttempts,
|
|
30
|
+
})
|
|
31
|
+
}}
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
v-for="outcome in report.outcomes"
|
|
36
|
+
:key="outcome.label"
|
|
37
|
+
class="rounded-md border border-slate-800 bg-slate-950/40 p-2"
|
|
38
|
+
data-testid="validation-outcome"
|
|
39
|
+
>
|
|
40
|
+
<div class="flex items-center gap-2">
|
|
41
|
+
<UIcon
|
|
42
|
+
:name="outcome.passed ? 'i-lucide-check' : 'i-lucide-x'"
|
|
43
|
+
class="h-3.5 w-3.5 shrink-0"
|
|
44
|
+
:class="outcome.passed ? 'text-emerald-400' : 'text-rose-400'"
|
|
45
|
+
/>
|
|
46
|
+
<span class="text-[12px] font-medium text-slate-200">{{ outcome.label }}</span>
|
|
47
|
+
<span class="truncate font-mono text-[11px] text-slate-500">{{ outcome.command }}</span>
|
|
48
|
+
<span
|
|
49
|
+
v-if="!outcome.passed"
|
|
50
|
+
class="ms-auto shrink-0 rounded bg-rose-500/15 px-1.5 py-0.5 text-[11px] tabular-nums text-rose-300"
|
|
51
|
+
>
|
|
52
|
+
{{
|
|
53
|
+
outcome.timedOut
|
|
54
|
+
? t('panels.stepDetail.validation.timedOut')
|
|
55
|
+
: t('panels.stepDetail.validation.exitCode', { code: outcome.exitCode })
|
|
56
|
+
}}
|
|
57
|
+
</span>
|
|
58
|
+
</div>
|
|
59
|
+
<pre
|
|
60
|
+
v-if="outcome.outputTail"
|
|
61
|
+
class="mt-1.5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded bg-slate-950 p-2 font-mono text-[11px] text-slate-400"
|
|
62
|
+
data-testid="validation-output"
|
|
63
|
+
>{{ outcome.outputTail }}</pre>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
|
+
import type { Block } from '~/types/domain'
|
|
4
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
5
|
+
import {
|
|
6
|
+
VALIDATION_DEFAULT_MAX_ATTEMPTS,
|
|
7
|
+
VALIDATION_MAX_ATTEMPTS_CEILING,
|
|
8
|
+
VALIDATION_MAX_CHECKS,
|
|
9
|
+
type ValidationCheck,
|
|
10
|
+
} from '~/types/validationChecks'
|
|
11
|
+
|
|
12
|
+
// Per-service (frame) PRE-PR VALIDATION CHECKS: the shell commands the executor-harness runs
|
|
13
|
+
// against the checkout after the coder settles and BEFORE the PR opens. A failing command's
|
|
14
|
+
// output is handed back to the agent, which retries under the attempt budget; only a green
|
|
15
|
+
// checkout opens a PR, and an exhausted budget fails the step with the captured output.
|
|
16
|
+
// Keyed by THIS block's id (service frames only — a task resolves its checks up the frame chain).
|
|
17
|
+
const props = defineProps<{ block: Block }>()
|
|
18
|
+
|
|
19
|
+
const store = useValidationChecksStore()
|
|
20
|
+
const toast = useToast()
|
|
21
|
+
const { t } = useI18n()
|
|
22
|
+
const { confirmAction, toastDone } = useConfirmAction()
|
|
23
|
+
|
|
24
|
+
const busy = ref(false)
|
|
25
|
+
const rows = ref<ValidationCheck[]>([])
|
|
26
|
+
const maxAttempts = ref(VALIDATION_DEFAULT_MAX_ATTEMPTS)
|
|
27
|
+
|
|
28
|
+
const saved = computed(() => store.forBlock(props.block.id))
|
|
29
|
+
const configured = computed(() => saved.value.checks.length > 0)
|
|
30
|
+
const canAdd = computed(() => rows.value.length < VALIDATION_MAX_CHECKS)
|
|
31
|
+
/** A row is only submittable once it has a command; the label falls back to the command. */
|
|
32
|
+
const submittable = computed(() =>
|
|
33
|
+
rows.value
|
|
34
|
+
.map((r) => ({ label: r.label.trim() || r.command.trim(), command: r.command.trim() }))
|
|
35
|
+
.filter((r) => r.command.length > 0),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
store.ensureLoaded().catch(() => {})
|
|
40
|
+
})
|
|
41
|
+
watch(
|
|
42
|
+
saved,
|
|
43
|
+
(config) => {
|
|
44
|
+
rows.value = config.checks.map((c) => ({ ...c }))
|
|
45
|
+
maxAttempts.value = config.maxAttempts
|
|
46
|
+
},
|
|
47
|
+
{ immediate: true },
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
function addRow() {
|
|
51
|
+
if (canAdd.value) rows.value.push({ label: '', command: '' })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function removeRow(index: number) {
|
|
55
|
+
rows.value.splice(index, 1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function notifyError(title: string, e: unknown) {
|
|
59
|
+
toast.add({
|
|
60
|
+
title,
|
|
61
|
+
description: e instanceof Error ? e.message : String(e),
|
|
62
|
+
icon: 'i-lucide-triangle-alert',
|
|
63
|
+
color: 'error',
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function save() {
|
|
68
|
+
busy.value = true
|
|
69
|
+
try {
|
|
70
|
+
await store.save(props.block.id, submittable.value, maxAttempts.value)
|
|
71
|
+
toast.add({
|
|
72
|
+
title: t('inspector.validationChecks.savedToast'),
|
|
73
|
+
icon: 'i-lucide-check',
|
|
74
|
+
color: 'success',
|
|
75
|
+
})
|
|
76
|
+
} catch (e) {
|
|
77
|
+
notifyError(t('inspector.validationChecks.saveFailed'), e)
|
|
78
|
+
} finally {
|
|
79
|
+
busy.value = false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function clear() {
|
|
84
|
+
const noun = t('inspector.validationChecks.configNoun')
|
|
85
|
+
if (!(await confirmAction('clear', noun))) return
|
|
86
|
+
busy.value = true
|
|
87
|
+
try {
|
|
88
|
+
await store.remove(props.block.id)
|
|
89
|
+
rows.value = []
|
|
90
|
+
toastDone('clear', noun)
|
|
91
|
+
} catch (e) {
|
|
92
|
+
notifyError(t('inspector.validationChecks.clearFailed'), e)
|
|
93
|
+
} finally {
|
|
94
|
+
busy.value = false
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<InspectorSection
|
|
101
|
+
v-if="store.available !== false"
|
|
102
|
+
:title="t('inspector.validationChecks.title')"
|
|
103
|
+
:hint="t('inspector.validationChecks.sectionHint')"
|
|
104
|
+
data-testid="service-validation-config"
|
|
105
|
+
>
|
|
106
|
+
<template #actions>
|
|
107
|
+
<UButton
|
|
108
|
+
v-if="configured"
|
|
109
|
+
color="error"
|
|
110
|
+
variant="ghost"
|
|
111
|
+
size="xs"
|
|
112
|
+
icon="i-lucide-trash-2"
|
|
113
|
+
:loading="busy"
|
|
114
|
+
data-testid="validation-clear"
|
|
115
|
+
@click="clear"
|
|
116
|
+
>
|
|
117
|
+
{{ t('inspector.validationChecks.clear') }}
|
|
118
|
+
</UButton>
|
|
119
|
+
</template>
|
|
120
|
+
|
|
121
|
+
<div class="space-y-2">
|
|
122
|
+
<p class="text-[11px] text-slate-500">
|
|
123
|
+
{{ t('inspector.validationChecks.hint') }}
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
<div
|
|
127
|
+
v-for="(row, index) in rows"
|
|
128
|
+
:key="index"
|
|
129
|
+
class="flex items-end gap-2"
|
|
130
|
+
data-testid="validation-check-row"
|
|
131
|
+
>
|
|
132
|
+
<UFormField :label="t('inspector.validationChecks.label')" class="w-1/3">
|
|
133
|
+
<UInput
|
|
134
|
+
v-model="row.label"
|
|
135
|
+
:placeholder="t('inspector.validationChecks.labelPlaceholder')"
|
|
136
|
+
size="sm"
|
|
137
|
+
class="w-full"
|
|
138
|
+
data-testid="validation-check-label"
|
|
139
|
+
/>
|
|
140
|
+
</UFormField>
|
|
141
|
+
<UFormField :label="t('inspector.validationChecks.command')" class="flex-1">
|
|
142
|
+
<UInput
|
|
143
|
+
v-model="row.command"
|
|
144
|
+
placeholder="pnpm lint"
|
|
145
|
+
size="sm"
|
|
146
|
+
class="w-full"
|
|
147
|
+
data-testid="validation-check-command"
|
|
148
|
+
/>
|
|
149
|
+
</UFormField>
|
|
150
|
+
<UButton
|
|
151
|
+
color="neutral"
|
|
152
|
+
variant="ghost"
|
|
153
|
+
size="xs"
|
|
154
|
+
icon="i-lucide-x"
|
|
155
|
+
:aria-label="t('inspector.validationChecks.removeCheck')"
|
|
156
|
+
data-testid="validation-check-remove"
|
|
157
|
+
@click="removeRow(index)"
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<p v-if="rows.length === 0" class="text-[11px] text-slate-500" data-testid="validation-empty">
|
|
162
|
+
{{ t('inspector.validationChecks.empty') }}
|
|
163
|
+
</p>
|
|
164
|
+
|
|
165
|
+
<div class="flex items-end justify-between gap-2">
|
|
166
|
+
<UFormField
|
|
167
|
+
:label="t('inspector.validationChecks.maxAttempts')"
|
|
168
|
+
:hint="t('inspector.validationChecks.maxAttemptsHint')"
|
|
169
|
+
class="w-40"
|
|
170
|
+
>
|
|
171
|
+
<UInput
|
|
172
|
+
v-model.number="maxAttempts"
|
|
173
|
+
type="number"
|
|
174
|
+
:min="1"
|
|
175
|
+
:max="VALIDATION_MAX_ATTEMPTS_CEILING"
|
|
176
|
+
size="sm"
|
|
177
|
+
class="w-full"
|
|
178
|
+
data-testid="validation-max-attempts"
|
|
179
|
+
/>
|
|
180
|
+
</UFormField>
|
|
181
|
+
<div class="flex gap-2">
|
|
182
|
+
<UButton
|
|
183
|
+
color="neutral"
|
|
184
|
+
variant="soft"
|
|
185
|
+
size="xs"
|
|
186
|
+
icon="i-lucide-plus"
|
|
187
|
+
:disabled="!canAdd"
|
|
188
|
+
data-testid="validation-add-check"
|
|
189
|
+
@click="addRow"
|
|
190
|
+
>
|
|
191
|
+
{{ t('inspector.validationChecks.addCheck') }}
|
|
192
|
+
</UButton>
|
|
193
|
+
<UButton
|
|
194
|
+
color="primary"
|
|
195
|
+
variant="soft"
|
|
196
|
+
size="xs"
|
|
197
|
+
icon="i-lucide-save"
|
|
198
|
+
:loading="busy"
|
|
199
|
+
data-testid="validation-save"
|
|
200
|
+
@click="save"
|
|
201
|
+
>
|
|
202
|
+
{{ t('inspector.validationChecks.save') }}
|
|
203
|
+
</UButton>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</InspectorSection>
|
|
208
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deleteServiceValidationConfigContract,
|
|
3
|
+
getServiceValidationConfigContract,
|
|
4
|
+
listServiceValidationConfigsContract,
|
|
5
|
+
setServiceValidationConfigContract,
|
|
6
|
+
} from '@cat-factory/contracts'
|
|
7
|
+
import type { UpsertServiceValidationConfigInput } from '~/types/validationChecks'
|
|
8
|
+
import type { ApiContext } from './context'
|
|
9
|
+
|
|
10
|
+
/** Pre-PR validation checks: the per-service-frame commands the harness runs before opening a PR. */
|
|
11
|
+
export function validationChecksApi({ send, ws }: ApiContext) {
|
|
12
|
+
return {
|
|
13
|
+
listServiceValidationConfigs: (workspaceId: string) =>
|
|
14
|
+
send(listServiceValidationConfigsContract, { pathPrefix: ws(workspaceId) }),
|
|
15
|
+
|
|
16
|
+
getServiceValidationConfig: (workspaceId: string, blockId: string) =>
|
|
17
|
+
send(getServiceValidationConfigContract, {
|
|
18
|
+
pathPrefix: ws(workspaceId),
|
|
19
|
+
pathParams: { blockId },
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
setServiceValidationConfig: (
|
|
23
|
+
workspaceId: string,
|
|
24
|
+
blockId: string,
|
|
25
|
+
body: UpsertServiceValidationConfigInput,
|
|
26
|
+
) =>
|
|
27
|
+
send(setServiceValidationConfigContract, {
|
|
28
|
+
pathPrefix: ws(workspaceId),
|
|
29
|
+
pathParams: { blockId },
|
|
30
|
+
body,
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
deleteServiceValidationConfig: (workspaceId: string, blockId: string) =>
|
|
34
|
+
send(deleteServiceValidationConfigContract, {
|
|
35
|
+
pathPrefix: ws(workspaceId),
|
|
36
|
+
pathParams: { blockId },
|
|
37
|
+
}),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -35,6 +35,7 @@ import { recurringApi } from './api/recurring'
|
|
|
35
35
|
import { previewApi } from './api/preview'
|
|
36
36
|
import { environmentsApi } from './api/environments'
|
|
37
37
|
import { releaseHealthApi } from './api/releaseHealth'
|
|
38
|
+
import { validationChecksApi } from './api/validationChecks'
|
|
38
39
|
import { sandboxApi } from './api/sandbox'
|
|
39
40
|
import { reviewsApi } from './api/reviews'
|
|
40
41
|
import { slackApi } from './api/slack'
|
|
@@ -135,6 +136,7 @@ export function useApi() {
|
|
|
135
136
|
...docInterviewApi(ctx),
|
|
136
137
|
...provisioningLogsApi(ctx),
|
|
137
138
|
...releaseHealthApi(ctx),
|
|
139
|
+
...validationChecksApi(ctx),
|
|
138
140
|
...testSecretsApi(ctx),
|
|
139
141
|
...packageRegistriesApi(ctx),
|
|
140
142
|
...previewApi(ctx),
|
|
@@ -41,6 +41,7 @@ describe('inspector panel group', () => {
|
|
|
41
41
|
'service-test-secrets',
|
|
42
42
|
'service-fragments',
|
|
43
43
|
'service-release-health',
|
|
44
|
+
'service-validation-checks',
|
|
44
45
|
])
|
|
45
46
|
})
|
|
46
47
|
|
|
@@ -59,6 +60,7 @@ describe('inspector panel group', () => {
|
|
|
59
60
|
'service-test-secrets',
|
|
60
61
|
'service-fragments',
|
|
61
62
|
'service-release-health',
|
|
63
|
+
'service-validation-checks',
|
|
62
64
|
])
|
|
63
65
|
})
|
|
64
66
|
|
|
@@ -70,6 +72,7 @@ describe('inspector panel group', () => {
|
|
|
70
72
|
'service-test-secrets',
|
|
71
73
|
'service-fragments',
|
|
72
74
|
'service-release-health',
|
|
75
|
+
'service-validation-checks',
|
|
73
76
|
])
|
|
74
77
|
})
|
|
75
78
|
|
|
@@ -53,6 +53,7 @@ export const INSPECTOR_PANEL_IDS = [
|
|
|
53
53
|
'service-test-secrets',
|
|
54
54
|
'service-fragments',
|
|
55
55
|
'service-release-health',
|
|
56
|
+
'service-validation-checks',
|
|
56
57
|
// epic / initiative body
|
|
57
58
|
'epic-children',
|
|
58
59
|
'initiative-inspector',
|
|
@@ -118,6 +119,10 @@ export const INSPECTOR_PANEL_SPECS: readonly InspectorPanelSpec[] = [
|
|
|
118
119
|
{ id: 'service-test-secrets', order: 150, when: isDeployableFrame },
|
|
119
120
|
{ id: 'service-fragments', order: 160, when: isFrame },
|
|
120
121
|
{ id: 'service-release-health', order: 170, when: isDeployableFrame },
|
|
122
|
+
// Pre-PR validation checks: the commands the harness runs before opening this service's PRs.
|
|
123
|
+
// Deployable frames only — a doc repo ships no build to install/lint/test, exactly like the
|
|
124
|
+
// test-infra and release-health panels above it.
|
|
125
|
+
{ id: 'service-validation-checks', order: 180, when: isDeployableFrame },
|
|
121
126
|
{ id: 'epic-children', order: 200, when: (b) => b.level === 'epic' },
|
|
122
127
|
{ id: 'initiative-inspector', order: 210, when: (b) => b.level === 'initiative' },
|
|
123
128
|
]
|
|
@@ -28,6 +28,7 @@ import ServiceTestConfig from '~/components/panels/inspector/ServiceTestConfig.v
|
|
|
28
28
|
import ServiceTestSecrets from '~/components/panels/inspector/ServiceTestSecrets.vue'
|
|
29
29
|
import ServiceFragments from '~/components/panels/inspector/ServiceFragments.vue'
|
|
30
30
|
import ServiceReleaseHealthConfig from '~/components/panels/inspector/ServiceReleaseHealthConfig.vue'
|
|
31
|
+
import ServiceValidationConfig from '~/components/panels/inspector/ServiceValidationConfig.vue'
|
|
31
32
|
import EpicChildren from '~/components/panels/inspector/EpicChildren.vue'
|
|
32
33
|
import InitiativeInspector from '~/components/panels/inspector/InitiativeInspector.vue'
|
|
33
34
|
|
|
@@ -80,6 +81,7 @@ const COMPONENTS: Record<InspectorPanelId, Component> = {
|
|
|
80
81
|
'service-test-secrets': ServiceTestSecrets,
|
|
81
82
|
'service-fragments': ServiceFragments,
|
|
82
83
|
'service-release-health': ServiceReleaseHealthConfig,
|
|
84
|
+
'service-validation-checks': ServiceValidationConfig,
|
|
83
85
|
'epic-children': EpicChildren,
|
|
84
86
|
'initiative-inspector': InitiativeInspector,
|
|
85
87
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import type { ServiceValidationConfig, ValidationCheck } from '~/types/validationChecks'
|
|
4
|
+
import { VALIDATION_DEFAULT_MAX_ATTEMPTS } from '~/types/validationChecks'
|
|
5
|
+
import { useUpsertList } from '~/composables/useUpsertList'
|
|
6
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The workspace's PRE-PR VALIDATION CHECKS: per service frame, the shell commands the
|
|
10
|
+
* executor-harness runs against the checkout after the coder settles and BEFORE the PR opens.
|
|
11
|
+
* A failing command is fed back into the agent loop; only a green checkout opens a PR.
|
|
12
|
+
*
|
|
13
|
+
* Loaded on demand by the service inspector rather than from the workspace snapshot — it's
|
|
14
|
+
* operator configuration read on one panel, not something every board load needs.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Whether a failed load is a SETTLED "you can't have this" — the facade wired no store (503) or
|
|
18
|
+
* this user lacks `settings.manage` (403) — rather than a transient fault. Only the settled cases
|
|
19
|
+
* latch `available` to false; everything else stays unresolved so a later visit retries.
|
|
20
|
+
*/
|
|
21
|
+
function isSettledUnavailable(error: unknown): boolean {
|
|
22
|
+
const status =
|
|
23
|
+
error && typeof error === 'object' && 'statusCode' in error
|
|
24
|
+
? (error as { statusCode?: number }).statusCode
|
|
25
|
+
: undefined
|
|
26
|
+
return status === 503 || status === 403
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const useValidationChecksStore = defineStore('validationChecks', () => {
|
|
30
|
+
const api = useApi()
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
items: configs,
|
|
34
|
+
upsert: upsertLocal,
|
|
35
|
+
remove: dropLocal,
|
|
36
|
+
} = useUpsertList<ServiceValidationConfig>({ key: (c) => c.blockId })
|
|
37
|
+
const loading = ref(false)
|
|
38
|
+
/**
|
|
39
|
+
* Mirrors the backend's wiring: `null` until first probed, then true/false. The inspector
|
|
40
|
+
* panel hides itself when false, so a facade that didn't wire the store shows no dead control.
|
|
41
|
+
*/
|
|
42
|
+
const available = ref<boolean | null>(null)
|
|
43
|
+
let inFlight: Promise<void> | null = null
|
|
44
|
+
|
|
45
|
+
/** Force a refresh of every configured service's checks (used after a save/remove). */
|
|
46
|
+
async function load() {
|
|
47
|
+
const ws = useWorkspaceStore()
|
|
48
|
+
loading.value = true
|
|
49
|
+
try {
|
|
50
|
+
configs.value = await api.listServiceValidationConfigs(ws.requireId())
|
|
51
|
+
available.value = true
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// A 503 means the facade wired no store and a 403 means this user may not manage settings:
|
|
54
|
+
// both are STABLE answers, so latch them and hide the entry point rather than showing a
|
|
55
|
+
// dead control. Anything else (a network blip, a 5xx) is TRANSIENT — leaving `available`
|
|
56
|
+
// unresolved so the next `ensureLoaded` retries, instead of hiding the panel for the rest
|
|
57
|
+
// of the session over one dropped request.
|
|
58
|
+
available.value = isSettledUnavailable(error) ? false : null
|
|
59
|
+
} finally {
|
|
60
|
+
loading.value = false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Load once per session; concurrent callers share the in-flight request. */
|
|
65
|
+
async function ensureLoaded(): Promise<void> {
|
|
66
|
+
if (available.value !== null) return
|
|
67
|
+
inFlight ??= load().finally(() => {
|
|
68
|
+
inFlight = null
|
|
69
|
+
})
|
|
70
|
+
return inFlight
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The checks configured for a service frame (an empty default when it has none). */
|
|
74
|
+
function forBlock(blockId: string): ServiceValidationConfig {
|
|
75
|
+
return (
|
|
76
|
+
configs.value.find((c) => c.blockId === blockId) ?? {
|
|
77
|
+
blockId,
|
|
78
|
+
checks: [],
|
|
79
|
+
maxAttempts: VALIDATION_DEFAULT_MAX_ATTEMPTS,
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Save a service frame's checks. An EMPTY list clears the config on the backend (the service
|
|
86
|
+
* deletes the row), which restores the exact pre-feature behaviour — so the local list drops
|
|
87
|
+
* the entry rather than keeping an empty one that reads as "configured".
|
|
88
|
+
*/
|
|
89
|
+
async function save(
|
|
90
|
+
blockId: string,
|
|
91
|
+
checks: ValidationCheck[],
|
|
92
|
+
maxAttempts: number,
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
const ws = useWorkspaceStore()
|
|
95
|
+
const saved = await api.setServiceValidationConfig(ws.requireId(), blockId, {
|
|
96
|
+
checks,
|
|
97
|
+
maxAttempts,
|
|
98
|
+
})
|
|
99
|
+
if (saved.checks.length === 0) dropLocal(blockId)
|
|
100
|
+
else upsertLocal(saved)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Remove a service frame's checks entirely. */
|
|
104
|
+
async function remove(blockId: string): Promise<void> {
|
|
105
|
+
const ws = useWorkspaceStore()
|
|
106
|
+
await api.deleteServiceValidationConfig(ws.requireId(), blockId)
|
|
107
|
+
dropLocal(blockId)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { configs, loading, available, load, ensureLoaded, forBlock, save, remove }
|
|
111
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Pre-PR validation-check shapes: the per-service-frame commands the executor-harness runs
|
|
2
|
+
// against the checkout before opening a PR, and the report it produces.
|
|
3
|
+
//
|
|
4
|
+
// All wire shapes are sourced from @cat-factory/contracts (single source of truth).
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
ValidationCheck,
|
|
8
|
+
ValidationCheckOutcome,
|
|
9
|
+
ValidationReport,
|
|
10
|
+
ServiceValidationConfig,
|
|
11
|
+
UpsertServiceValidationConfigInput,
|
|
12
|
+
} from '@cat-factory/contracts'
|
|
13
|
+
export {
|
|
14
|
+
VALIDATION_DEFAULT_MAX_ATTEMPTS,
|
|
15
|
+
VALIDATION_MAX_ATTEMPTS_CEILING,
|
|
16
|
+
VALIDATION_MAX_CHECKS,
|
|
17
|
+
} from '@cat-factory/contracts'
|
package/i18n/locales/de.json
CHANGED
|
@@ -1346,6 +1346,25 @@
|
|
|
1346
1346
|
"remove": "{repo} entfernen",
|
|
1347
1347
|
"connectFirst": "Verbinde GitHub, um Referenz-Repositories anzuhängen.",
|
|
1348
1348
|
"hint": "Der Dokument-Writer klont diese schreibgeschützt, um beim Entwerfen vorhandene Lösungen als Referenz wiederzuverwenden. Er ändert sie nie."
|
|
1349
|
+
},
|
|
1350
|
+
"validationChecks": {
|
|
1351
|
+
"title": "Prüfungen vor dem PR",
|
|
1352
|
+
"sectionHint": "Befehle, die nach dem Coder und vor dem Öffnen eines Pull Requests im Checkout laufen. Ein Fehlschlag geht zur Behebung an den Agenten zurück; nur ein fehlerfreier Checkout öffnet einen PR.",
|
|
1353
|
+
"hint": "Jeder Befehl läuft der Reihe nach mit `sh -c` im Checkout dieses Dienstes. Der Agent soll den Code reparieren, nicht die Prüfung abschwächen.",
|
|
1354
|
+
"clear": "Leeren",
|
|
1355
|
+
"configNoun": "Prüfungen",
|
|
1356
|
+
"label": "Name",
|
|
1357
|
+
"labelPlaceholder": "lint",
|
|
1358
|
+
"command": "Befehl",
|
|
1359
|
+
"addCheck": "Prüfung hinzufügen",
|
|
1360
|
+
"removeCheck": "Diese Prüfung entfernen",
|
|
1361
|
+
"empty": "Keine Prüfungen konfiguriert. Läufe für diesen Dienst öffnen einen Pull Request ohne lokale Überprüfung.",
|
|
1362
|
+
"maxAttempts": "Versuche",
|
|
1363
|
+
"maxAttemptsHint": "Korrekturrunden",
|
|
1364
|
+
"save": "Prüfungen speichern",
|
|
1365
|
+
"savedToast": "Prüfungen gespeichert",
|
|
1366
|
+
"saveFailed": "Prüfungen konnten nicht gespeichert werden",
|
|
1367
|
+
"clearFailed": "Prüfungen konnten nicht geleert werden"
|
|
1349
1368
|
}
|
|
1350
1369
|
},
|
|
1351
1370
|
"panels": {
|
|
@@ -1512,6 +1531,16 @@
|
|
|
1512
1531
|
"outOfTen": "{value}/10",
|
|
1513
1532
|
"relatedFindings": "Zugehörige Befunde",
|
|
1514
1533
|
"unnamed": "Standard"
|
|
1534
|
+
},
|
|
1535
|
+
"validation": {
|
|
1536
|
+
"heading": "Prüfungen vor dem PR",
|
|
1537
|
+
"passed": "Bestanden",
|
|
1538
|
+
"failed": "Fehlgeschlagen",
|
|
1539
|
+
"attempts": "Versuch {attempts} von {maxAttempts}",
|
|
1540
|
+
"passedSummary": "Alle {total} Prüfungen wurden in Versuch {attempts} bestanden. Der Pull Request wurde aus einem fehlerfreien Checkout geöffnet.",
|
|
1541
|
+
"failedSummary": "{failed} von {total} Prüfungen schlugen nach {attempts} von {maxAttempts} Versuchen weiterhin fehl, daher wurde kein Pull Request geöffnet.",
|
|
1542
|
+
"exitCode": "Exit-Code {code}",
|
|
1543
|
+
"timedOut": "Zeitüberschreitung"
|
|
1515
1544
|
}
|
|
1516
1545
|
},
|
|
1517
1546
|
"inspector": {
|
package/i18n/locales/en.json
CHANGED
|
@@ -1117,6 +1117,25 @@
|
|
|
1117
1117
|
"remove": "Remove {repo}",
|
|
1118
1118
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1119
1119
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1120
|
+
},
|
|
1121
|
+
"validationChecks": {
|
|
1122
|
+
"title": "Pre-PR validation",
|
|
1123
|
+
"sectionHint": "Commands run against the checkout after the coder finishes and before a pull request is opened. A failure is handed back to the agent to fix; only a passing checkout opens a PR.",
|
|
1124
|
+
"hint": "Each command runs with `sh -c` in this service's checkout, in order. Fix the code, not the check — the agent is told not to weaken them.",
|
|
1125
|
+
"clear": "Clear",
|
|
1126
|
+
"configNoun": "validation checks",
|
|
1127
|
+
"label": "Name",
|
|
1128
|
+
"labelPlaceholder": "lint",
|
|
1129
|
+
"command": "Command",
|
|
1130
|
+
"addCheck": "Add check",
|
|
1131
|
+
"removeCheck": "Remove this check",
|
|
1132
|
+
"empty": "No checks configured. Runs for this service open a pull request without any local verification.",
|
|
1133
|
+
"maxAttempts": "Attempts",
|
|
1134
|
+
"maxAttemptsHint": "Rounds of fixing",
|
|
1135
|
+
"save": "Save checks",
|
|
1136
|
+
"savedToast": "Validation checks saved",
|
|
1137
|
+
"saveFailed": "Could not save the validation checks",
|
|
1138
|
+
"clearFailed": "Could not clear the validation checks"
|
|
1120
1139
|
}
|
|
1121
1140
|
},
|
|
1122
1141
|
"panels": {
|
|
@@ -1283,6 +1302,16 @@
|
|
|
1283
1302
|
"outOfTen": "{value}/10",
|
|
1284
1303
|
"relatedFindings": "Related findings",
|
|
1285
1304
|
"unnamed": "Standard"
|
|
1305
|
+
},
|
|
1306
|
+
"validation": {
|
|
1307
|
+
"heading": "Pre-PR validation",
|
|
1308
|
+
"passed": "Passed",
|
|
1309
|
+
"failed": "Failed",
|
|
1310
|
+
"attempts": "Attempt {attempts} of {maxAttempts}",
|
|
1311
|
+
"passedSummary": "All {total} checks passed on attempt {attempts}. The pull request was opened from a green checkout.",
|
|
1312
|
+
"failedSummary": "{failed} of {total} checks were still failing after {attempts} of {maxAttempts} attempts, so no pull request was opened.",
|
|
1313
|
+
"exitCode": "exit {code}",
|
|
1314
|
+
"timedOut": "timed out"
|
|
1286
1315
|
}
|
|
1287
1316
|
},
|
|
1288
1317
|
"inspector": {
|
package/i18n/locales/es.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Quitar {repo}",
|
|
1061
1061
|
"connectFirst": "Conecta GitHub para adjuntar repositorios de referencia.",
|
|
1062
1062
|
"hint": "El escritor de documentos los clona en modo de solo lectura para reutilizar soluciones existentes como referencia al redactar. Nunca los modifica."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "Validación previa al PR",
|
|
1066
|
+
"sectionHint": "Comandos que se ejecutan sobre la copia de trabajo cuando el programador termina y antes de abrir una solicitud de incorporación. Un fallo vuelve al agente para que lo corrija; solo una copia sin errores abre un PR.",
|
|
1067
|
+
"hint": "Cada comando se ejecuta con `sh -c` en la copia de trabajo de este servicio, en orden. Hay que arreglar el código, no la comprobación: al agente se le indica que no las debilite.",
|
|
1068
|
+
"clear": "Vaciar",
|
|
1069
|
+
"configNoun": "comprobaciones de validación",
|
|
1070
|
+
"label": "Nombre",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "Comando",
|
|
1073
|
+
"addCheck": "Añadir comprobación",
|
|
1074
|
+
"removeCheck": "Quitar esta comprobación",
|
|
1075
|
+
"empty": "No hay comprobaciones configuradas. Las ejecuciones de este servicio abren una solicitud de incorporación sin verificación local.",
|
|
1076
|
+
"maxAttempts": "Intentos",
|
|
1077
|
+
"maxAttemptsHint": "Rondas de corrección",
|
|
1078
|
+
"save": "Guardar comprobaciones",
|
|
1079
|
+
"savedToast": "Comprobaciones guardadas",
|
|
1080
|
+
"saveFailed": "No se pudieron guardar las comprobaciones",
|
|
1081
|
+
"clearFailed": "No se pudieron vaciar las comprobaciones"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "Hallazgos relacionados",
|
|
1228
1247
|
"unnamed": "Estándar"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "Validación previa al PR",
|
|
1251
|
+
"passed": "Superada",
|
|
1252
|
+
"failed": "Fallida",
|
|
1253
|
+
"attempts": "Intento {attempts} de {maxAttempts}",
|
|
1254
|
+
"passedSummary": "Las {total} comprobaciones se superaron en el intento {attempts}. La solicitud de incorporación se abrió desde una copia sin errores.",
|
|
1255
|
+
"failedSummary": "{failed} de {total} comprobaciones seguían fallando tras {attempts} de {maxAttempts} intentos, así que no se abrió ninguna solicitud de incorporación.",
|
|
1256
|
+
"exitCode": "código de salida {code}",
|
|
1257
|
+
"timedOut": "tiempo agotado"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Retirer {repo}",
|
|
1061
1061
|
"connectFirst": "Connectez GitHub pour joindre des dépôts de référence.",
|
|
1062
1062
|
"hint": "Le rédacteur de documents les clone en lecture seule pour réutiliser des solutions existantes comme référence lors de la rédaction. Il ne les modifie jamais."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "Validation avant la PR",
|
|
1066
|
+
"sectionHint": "Commandes exécutées sur la copie de travail une fois le développeur terminé et avant l'ouverture d'une demande de tirage. Un échec est renvoyé à l'agent pour correction ; seule une copie sans erreur ouvre une PR.",
|
|
1067
|
+
"hint": "Chaque commande s'exécute avec `sh -c` dans la copie de travail de ce service, dans l'ordre. Il faut corriger le code, pas la vérification : il est demandé à l'agent de ne pas les affaiblir.",
|
|
1068
|
+
"clear": "Vider",
|
|
1069
|
+
"configNoun": "vérifications de validation",
|
|
1070
|
+
"label": "Nom",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "Commande",
|
|
1073
|
+
"addCheck": "Ajouter une vérification",
|
|
1074
|
+
"removeCheck": "Supprimer cette vérification",
|
|
1075
|
+
"empty": "Aucune vérification configurée. Les exécutions de ce service ouvrent une demande de tirage sans vérification locale.",
|
|
1076
|
+
"maxAttempts": "Tentatives",
|
|
1077
|
+
"maxAttemptsHint": "Cycles de correction",
|
|
1078
|
+
"save": "Enregistrer les vérifications",
|
|
1079
|
+
"savedToast": "Vérifications enregistrées",
|
|
1080
|
+
"saveFailed": "Impossible d'enregistrer les vérifications",
|
|
1081
|
+
"clearFailed": "Impossible de vider les vérifications"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "Constats associés",
|
|
1228
1247
|
"unnamed": "Standard"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "Validation avant la PR",
|
|
1251
|
+
"passed": "Réussie",
|
|
1252
|
+
"failed": "Échouée",
|
|
1253
|
+
"attempts": "Tentative {attempts} sur {maxAttempts}",
|
|
1254
|
+
"passedSummary": "Les {total} vérifications ont réussi à la tentative {attempts}. La demande de tirage a été ouverte depuis une copie sans erreur.",
|
|
1255
|
+
"failedSummary": "{failed} vérifications sur {total} échouaient encore après {attempts} tentatives sur {maxAttempts}, aucune demande de tirage n'a donc été ouverte.",
|
|
1256
|
+
"exitCode": "code de sortie {code}",
|
|
1257
|
+
"timedOut": "délai dépassé"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/he.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Remove {repo}",
|
|
1061
1061
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1062
1062
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "בדיקות לפני בקשת משיכה",
|
|
1066
|
+
"sectionHint": "פקודות שרצות על העותק המקומי אחרי שהמפתח מסיים ולפני פתיחת בקשת משיכה. כישלון מוחזר לסוכן לתיקון; רק עותק תקין פותח בקשת משיכה.",
|
|
1067
|
+
"hint": "כל פקודה רצה עם `sh -c` בעותק המקומי של שירות זה, לפי הסדר. יש לתקן את הקוד ולא את הבדיקה — הסוכן מונחה לא להחליש אותן.",
|
|
1068
|
+
"clear": "ניקוי",
|
|
1069
|
+
"configNoun": "בדיקות אימות",
|
|
1070
|
+
"label": "שם",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "פקודה",
|
|
1073
|
+
"addCheck": "הוספת בדיקה",
|
|
1074
|
+
"removeCheck": "הסרת בדיקה זו",
|
|
1075
|
+
"empty": "לא הוגדרו בדיקות. הרצות של שירות זה פותחות בקשת משיכה ללא אימות מקומי.",
|
|
1076
|
+
"maxAttempts": "ניסיונות",
|
|
1077
|
+
"maxAttemptsHint": "סבבי תיקון",
|
|
1078
|
+
"save": "שמירת בדיקות",
|
|
1079
|
+
"savedToast": "הבדיקות נשמרו",
|
|
1080
|
+
"saveFailed": "לא ניתן היה לשמור את הבדיקות",
|
|
1081
|
+
"clearFailed": "לא ניתן היה לנקות את הבדיקות"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "ממצאים קשורים",
|
|
1228
1247
|
"unnamed": "תקן"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "בדיקות לפני בקשת משיכה",
|
|
1251
|
+
"passed": "עברו",
|
|
1252
|
+
"failed": "נכשלו",
|
|
1253
|
+
"attempts": "ניסיון {attempts} מתוך {maxAttempts}",
|
|
1254
|
+
"passedSummary": "כל {total} הבדיקות עברו בניסיון {attempts}. בקשת המשיכה נפתחה מעותק תקין.",
|
|
1255
|
+
"failedSummary": "{failed} מתוך {total} בדיקות עדיין נכשלו אחרי {attempts} מתוך {maxAttempts} ניסיונות, ולכן לא נפתחה בקשת משיכה.",
|
|
1256
|
+
"exitCode": "קוד יציאה {code}",
|
|
1257
|
+
"timedOut": "חריגת זמן"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/it.json
CHANGED
|
@@ -1346,6 +1346,25 @@
|
|
|
1346
1346
|
"remove": "Rimuovi {repo}",
|
|
1347
1347
|
"connectFirst": "Collega GitHub per allegare repository di riferimento.",
|
|
1348
1348
|
"hint": "Il document writer li clona in sola lettura per riutilizzare soluzioni esistenti come riferimento durante la stesura. Non li modifica mai."
|
|
1349
|
+
},
|
|
1350
|
+
"validationChecks": {
|
|
1351
|
+
"title": "Validazione prima della PR",
|
|
1352
|
+
"sectionHint": "Comandi eseguiti sulla copia di lavoro quando lo sviluppatore ha finito e prima di aprire una richiesta di modifica. Un errore torna all'agente perché lo corregga; solo una copia senza errori apre una PR.",
|
|
1353
|
+
"hint": "Ogni comando viene eseguito con `sh -c` nella copia di lavoro di questo servizio, in ordine. Va corretto il codice, non il controllo: all'agente viene chiesto di non indebolirli.",
|
|
1354
|
+
"clear": "Svuota",
|
|
1355
|
+
"configNoun": "controlli di validazione",
|
|
1356
|
+
"label": "Nome",
|
|
1357
|
+
"labelPlaceholder": "lint",
|
|
1358
|
+
"command": "Comando",
|
|
1359
|
+
"addCheck": "Aggiungi controllo",
|
|
1360
|
+
"removeCheck": "Rimuovi questo controllo",
|
|
1361
|
+
"empty": "Nessun controllo configurato. Le esecuzioni di questo servizio aprono una richiesta di modifica senza verifica locale.",
|
|
1362
|
+
"maxAttempts": "Tentativi",
|
|
1363
|
+
"maxAttemptsHint": "Cicli di correzione",
|
|
1364
|
+
"save": "Salva controlli",
|
|
1365
|
+
"savedToast": "Controlli salvati",
|
|
1366
|
+
"saveFailed": "Impossibile salvare i controlli",
|
|
1367
|
+
"clearFailed": "Impossibile svuotare i controlli"
|
|
1349
1368
|
}
|
|
1350
1369
|
},
|
|
1351
1370
|
"panels": {
|
|
@@ -1512,6 +1531,16 @@
|
|
|
1512
1531
|
"outOfTen": "{value}/10",
|
|
1513
1532
|
"relatedFindings": "Rilievi correlati",
|
|
1514
1533
|
"unnamed": "Standard"
|
|
1534
|
+
},
|
|
1535
|
+
"validation": {
|
|
1536
|
+
"heading": "Validazione prima della PR",
|
|
1537
|
+
"passed": "Superata",
|
|
1538
|
+
"failed": "Fallita",
|
|
1539
|
+
"attempts": "Tentativo {attempts} di {maxAttempts}",
|
|
1540
|
+
"passedSummary": "Tutti i {total} controlli sono stati superati al tentativo {attempts}. La richiesta di modifica è stata aperta da una copia senza errori.",
|
|
1541
|
+
"failedSummary": "{failed} controlli su {total} fallivano ancora dopo {attempts} tentativi su {maxAttempts}, quindi non è stata aperta alcuna richiesta di modifica.",
|
|
1542
|
+
"exitCode": "codice di uscita {code}",
|
|
1543
|
+
"timedOut": "tempo scaduto"
|
|
1515
1544
|
}
|
|
1516
1545
|
},
|
|
1517
1546
|
"inspector": {
|
package/i18n/locales/ja.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Remove {repo}",
|
|
1061
1061
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1062
1062
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "PR 前の検証",
|
|
1066
|
+
"sectionHint": "コーダーの作業完了後、プルリクエストを開く前にチェックアウトに対して実行されるコマンドです。失敗した場合はエージェントに差し戻して修正させ、問題のないチェックアウトだけが PR を開きます。",
|
|
1067
|
+
"hint": "各コマンドはこのサービスのチェックアウト内で `sh -c` により順番に実行されます。修正すべきはコードであり検査ではありません。エージェントには検査を弱めないよう指示しています。",
|
|
1068
|
+
"clear": "消去",
|
|
1069
|
+
"configNoun": "検証コマンド",
|
|
1070
|
+
"label": "名前",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "コマンド",
|
|
1073
|
+
"addCheck": "検査を追加",
|
|
1074
|
+
"removeCheck": "この検査を削除",
|
|
1075
|
+
"empty": "検査が設定されていません。このサービスの実行はローカル検証なしでプルリクエストを開きます。",
|
|
1076
|
+
"maxAttempts": "試行回数",
|
|
1077
|
+
"maxAttemptsHint": "修正の反復回数",
|
|
1078
|
+
"save": "検査を保存",
|
|
1079
|
+
"savedToast": "検査を保存しました",
|
|
1080
|
+
"saveFailed": "検査を保存できませんでした",
|
|
1081
|
+
"clearFailed": "検査を消去できませんでした"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "関連する指摘",
|
|
1228
1247
|
"unnamed": "基準"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "PR 前の検証",
|
|
1251
|
+
"passed": "成功",
|
|
1252
|
+
"failed": "失敗",
|
|
1253
|
+
"attempts": "試行 {attempts}/{maxAttempts}",
|
|
1254
|
+
"passedSummary": "{total} 件すべての検査が {attempts} 回目の試行で成功しました。プルリクエストは問題のないチェックアウトから開かれました。",
|
|
1255
|
+
"failedSummary": "{maxAttempts} 回中 {attempts} 回試行しても {total} 件中 {failed} 件の検査が失敗したままだったため、プルリクエストは開かれませんでした。",
|
|
1256
|
+
"exitCode": "終了コード {code}",
|
|
1257
|
+
"timedOut": "タイムアウト"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/pl.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Remove {repo}",
|
|
1061
1061
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1062
1062
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "Walidacja przed PR",
|
|
1066
|
+
"sectionHint": "Polecenia uruchamiane na kopii roboczej po zakończeniu pracy programisty, a przed otwarciem żądania scalenia. Niepowodzenie wraca do agenta do naprawy; tylko czysta kopia otwiera żądanie scalenia.",
|
|
1067
|
+
"hint": "Każde polecenie jest uruchamiane przez `sh -c` w kopii roboczej tej usługi, po kolei. Poprawiaj kod, a nie sprawdzenie — agent ma polecenie ich nie osłabiać.",
|
|
1068
|
+
"clear": "Wyczyść",
|
|
1069
|
+
"configNoun": "sprawdzenia walidacyjne",
|
|
1070
|
+
"label": "Nazwa",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "Polecenie",
|
|
1073
|
+
"addCheck": "Dodaj sprawdzenie",
|
|
1074
|
+
"removeCheck": "Usuń to sprawdzenie",
|
|
1075
|
+
"empty": "Nie skonfigurowano żadnych sprawdzeń. Uruchomienia tej usługi otwierają żądanie scalenia bez lokalnej weryfikacji.",
|
|
1076
|
+
"maxAttempts": "Próby",
|
|
1077
|
+
"maxAttemptsHint": "Rundy poprawek",
|
|
1078
|
+
"save": "Zapisz sprawdzenia",
|
|
1079
|
+
"savedToast": "Sprawdzenia zapisane",
|
|
1080
|
+
"saveFailed": "Nie udało się zapisać sprawdzeń",
|
|
1081
|
+
"clearFailed": "Nie udało się wyczyścić sprawdzeń"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "Powiązane ustalenia",
|
|
1228
1247
|
"unnamed": "Standard"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "Walidacja przed PR",
|
|
1251
|
+
"passed": "Zaliczona",
|
|
1252
|
+
"failed": "Niezaliczona",
|
|
1253
|
+
"attempts": "Próba {attempts} z {maxAttempts}",
|
|
1254
|
+
"passedSummary": "Wszystkie sprawdzenia ({total}) przeszły w próbie {attempts}. Żądanie scalenia zostało otwarte z czystej kopii roboczej.",
|
|
1255
|
+
"failedSummary": "Po {attempts} z {maxAttempts} prób nadal nie przechodziło {failed} z {total} sprawdzeń, więc nie otwarto żądania scalenia.",
|
|
1256
|
+
"exitCode": "kod wyjścia {code}",
|
|
1257
|
+
"timedOut": "przekroczono czas"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/tr.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Remove {repo}",
|
|
1061
1061
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1062
1062
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "PR öncesi doğrulama",
|
|
1066
|
+
"sectionHint": "Kodlayıcı işini bitirdikten sonra ve bir çekme isteği açılmadan önce çalışma kopyası üzerinde çalıştırılan komutlar. Bir hata düzeltilmesi için aracıya geri verilir; yalnızca sorunsuz bir çalışma kopyası çekme isteği açar.",
|
|
1067
|
+
"hint": "Her komut bu hizmetin çalışma kopyasında sırayla `sh -c` ile çalıştırılır. Denetimi değil kodu düzeltin — aracıya denetimleri zayıflatmaması söylenir.",
|
|
1068
|
+
"clear": "Temizle",
|
|
1069
|
+
"configNoun": "doğrulama denetimleri",
|
|
1070
|
+
"label": "Ad",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "Komut",
|
|
1073
|
+
"addCheck": "Denetim ekle",
|
|
1074
|
+
"removeCheck": "Bu denetimi kaldır",
|
|
1075
|
+
"empty": "Yapılandırılmış denetim yok. Bu hizmetin çalıştırmaları yerel doğrulama olmadan çekme isteği açar.",
|
|
1076
|
+
"maxAttempts": "Deneme",
|
|
1077
|
+
"maxAttemptsHint": "Düzeltme turu",
|
|
1078
|
+
"save": "Denetimleri kaydet",
|
|
1079
|
+
"savedToast": "Denetimler kaydedildi",
|
|
1080
|
+
"saveFailed": "Denetimler kaydedilemedi",
|
|
1081
|
+
"clearFailed": "Denetimler temizlenemedi"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "İlgili bulgular",
|
|
1228
1247
|
"unnamed": "Standart"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "PR öncesi doğrulama",
|
|
1251
|
+
"passed": "Geçti",
|
|
1252
|
+
"failed": "Başarısız",
|
|
1253
|
+
"attempts": "Deneme {attempts}/{maxAttempts}",
|
|
1254
|
+
"passedSummary": "{total} denetimin tamamı {attempts}. denemede geçti. Çekme isteği sorunsuz bir çalışma kopyasından açıldı.",
|
|
1255
|
+
"failedSummary": "{maxAttempts} denemenin {attempts} tanesinden sonra {total} denetimden {failed} tanesi hâlâ başarısızdı, bu yüzden çekme isteği açılmadı.",
|
|
1256
|
+
"exitCode": "çıkış kodu {code}",
|
|
1257
|
+
"timedOut": "zaman aşımı"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1060,6 +1060,25 @@
|
|
|
1060
1060
|
"remove": "Remove {repo}",
|
|
1061
1061
|
"connectFirst": "Connect GitHub to attach reference repositories.",
|
|
1062
1062
|
"hint": "The document writer clones these read-only to reuse existing solutions as a reference while drafting. It never changes them."
|
|
1063
|
+
},
|
|
1064
|
+
"validationChecks": {
|
|
1065
|
+
"title": "Перевірки перед PR",
|
|
1066
|
+
"sectionHint": "Команди, які виконуються на робочій копії після завершення роботи розробника та перед відкриттям запиту на злиття. Невдача повертається до агента на виправлення; лише чиста копія відкриває запит на злиття.",
|
|
1067
|
+
"hint": "Кожна команда виконується через `sh -c` у робочій копії цієї служби, по черзі. Виправляйте код, а не перевірку — агенту наказано не послаблювати їх.",
|
|
1068
|
+
"clear": "Очистити",
|
|
1069
|
+
"configNoun": "перевірки",
|
|
1070
|
+
"label": "Назва",
|
|
1071
|
+
"labelPlaceholder": "lint",
|
|
1072
|
+
"command": "Команда",
|
|
1073
|
+
"addCheck": "Додати перевірку",
|
|
1074
|
+
"removeCheck": "Вилучити цю перевірку",
|
|
1075
|
+
"empty": "Перевірок не налаштовано. Запуски цієї служби відкривають запит на злиття без локальної перевірки.",
|
|
1076
|
+
"maxAttempts": "Спроби",
|
|
1077
|
+
"maxAttemptsHint": "Раунди виправлень",
|
|
1078
|
+
"save": "Зберегти перевірки",
|
|
1079
|
+
"savedToast": "Перевірки збережено",
|
|
1080
|
+
"saveFailed": "Не вдалося зберегти перевірки",
|
|
1081
|
+
"clearFailed": "Не вдалося очистити перевірки"
|
|
1063
1082
|
}
|
|
1064
1083
|
},
|
|
1065
1084
|
"panels": {
|
|
@@ -1226,6 +1245,16 @@
|
|
|
1226
1245
|
"outOfTen": "{value}/10",
|
|
1227
1246
|
"relatedFindings": "Пов’язані зауваження",
|
|
1228
1247
|
"unnamed": "Стандарт"
|
|
1248
|
+
},
|
|
1249
|
+
"validation": {
|
|
1250
|
+
"heading": "Перевірки перед PR",
|
|
1251
|
+
"passed": "Пройдено",
|
|
1252
|
+
"failed": "Не пройдено",
|
|
1253
|
+
"attempts": "Спроба {attempts} з {maxAttempts}",
|
|
1254
|
+
"passedSummary": "Усі перевірки ({total}) пройшли у спробі {attempts}. Запит на злиття відкрито з чистої робочої копії.",
|
|
1255
|
+
"failedSummary": "Після {attempts} із {maxAttempts} спроб {failed} із {total} перевірок усе ще не проходили, тому запит на злиття не відкрито.",
|
|
1256
|
+
"exitCode": "код виходу {code}",
|
|
1257
|
+
"timedOut": "перевищено час"
|
|
1229
1258
|
}
|
|
1230
1259
|
},
|
|
1231
1260
|
"inspector": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.155.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.40",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.
|
|
43
|
+
"@cat-factory/contracts": "0.164.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|