@cat-factory/app 0.255.0 → 0.256.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/README.md +19 -2
- package/app/components/pipeline/PipelineBuilder.vue +50 -81
- package/app/composables/usePipelineDraftWarnings.ts +136 -0
- package/app/modular/agent-kinds.ts +3 -0
- package/app/modular/capabilities.spec.ts +49 -0
- package/app/modular/capabilities.ts +29 -26
- package/app/stores/pipelines/draftRetainEnvironment.spec.ts +62 -0
- package/app/stores/pipelines/draftStepConfig.ts +57 -27
- package/app/types/domain.ts +7 -1
- package/app/utils/agentPalette.spec.ts +30 -0
- package/app/utils/agentPalette.ts +11 -10
- package/app/utils/catalog.spec.ts +35 -0
- package/app/utils/catalog.ts +38 -6
- package/app/utils/pipeline.spec.ts +69 -0
- package/i18n/locales/de.json +7 -0
- package/i18n/locales/en.json +7 -0
- package/i18n/locales/es.json +7 -0
- package/i18n/locales/fr.json +7 -0
- package/i18n/locales/he.json +7 -0
- package/i18n/locales/it.json +7 -0
- package/i18n/locales/ja.json +7 -0
- package/i18n/locales/pl.json +7 -0
- package/i18n/locales/tr.json +7 -0
- package/i18n/locales/uk.json +7 -0
- package/package.json +2 -2
|
@@ -15,6 +15,27 @@ import { defaultConsensusConfig, type PipelinesContext } from './context'
|
|
|
15
15
|
* touches nothing else, which is what makes the two independent; both are spread into the store,
|
|
16
16
|
* so the store's API is unchanged.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Write ONE field of the step's `StepOptions` bag at `index`, merging into whatever else that step
|
|
20
|
+
* carries and normalizing an emptied bag back to `null`. `undefined` CLEARS the field.
|
|
21
|
+
*
|
|
22
|
+
* Every per-step option below goes through this rather than repeating the clone/assign/normalize
|
|
23
|
+
* dance, because the two halves that are easy to get wrong are shared by all of them: replacing
|
|
24
|
+
* the bag loses the neighbouring options a step may also carry, and leaving a `{}` behind makes a
|
|
25
|
+
* step that is back on every default persist a shape it never had.
|
|
26
|
+
*/
|
|
27
|
+
function patchStepOption<K extends keyof StepOptions>(
|
|
28
|
+
draftStepOptions: PipelinesContext['draftStepOptions'],
|
|
29
|
+
index: number,
|
|
30
|
+
key: K,
|
|
31
|
+
value: StepOptions[K] | undefined,
|
|
32
|
+
) {
|
|
33
|
+
const next: StepOptions = { ...draftStepOptions.value[index] }
|
|
34
|
+
if (value === undefined) delete next[key]
|
|
35
|
+
else next[key] = value
|
|
36
|
+
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
19
40
|
const {
|
|
20
41
|
draftGates,
|
|
@@ -118,10 +139,27 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
118
139
|
* flag. Merges with any other future StepOptions fields rather than clobbering the whole bag.
|
|
119
140
|
*/
|
|
120
141
|
function toggleDraftAutoRecommend(index: number) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
142
|
+
const off = draftAutoRecommendEnabled(index) ? false : undefined
|
|
143
|
+
patchStepOption(draftStepOptions, index, 'autoRecommend', off)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Whether the draft `deployer` step at `index` declares that its environments outlive the run
|
|
148
|
+
* (its `stepOptions.retainEnvironment`). OFF by default: the everyday shape is a run that
|
|
149
|
+
* reclaims what it stood up, and the save boundary refuses a Deployer that does neither.
|
|
150
|
+
*/
|
|
151
|
+
function draftRetainEnvironment(index: number): boolean {
|
|
152
|
+
return draftStepOptions.value[index]?.retainEnvironment === true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Toggle the retain declaration on the draft `deployer` step at `index`. It is off by default,
|
|
157
|
+
* so we store ONLY the opt-in (the mirror of `toggleDraftAutoRecommend`, which stores only the
|
|
158
|
+
* opt-out); toggling back drops the flag and, if the bag empties, the whole entry.
|
|
159
|
+
*/
|
|
160
|
+
function toggleDraftRetainEnvironment(index: number) {
|
|
161
|
+
const on = draftRetainEnvironment(index) ? undefined : true
|
|
162
|
+
patchStepOption(draftStepOptions, index, 'retainEnvironment', on)
|
|
125
163
|
}
|
|
126
164
|
|
|
127
165
|
/** The skill picked for the draft `skill` step at `index` (its `stepOptions.skillId`). */
|
|
@@ -135,10 +173,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
135
173
|
* bag empties, the whole entry (so it normalizes away like the other options).
|
|
136
174
|
*/
|
|
137
175
|
function setDraftSkillId(index: number, skillId: string | undefined) {
|
|
138
|
-
|
|
139
|
-
if (skillId) next.skillId = skillId
|
|
140
|
-
else delete next.skillId
|
|
141
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
176
|
+
patchStepOption(draftStepOptions, index, 'skillId', skillId || undefined)
|
|
142
177
|
}
|
|
143
178
|
|
|
144
179
|
/**
|
|
@@ -156,10 +191,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
156
191
|
* shipped prompt persists nothing.
|
|
157
192
|
*/
|
|
158
193
|
function setDraftAgentVariantId(index: number, agentVariantId: string | undefined) {
|
|
159
|
-
|
|
160
|
-
if (agentVariantId) next.agentVariantId = agentVariantId
|
|
161
|
-
else delete next.agentVariantId
|
|
162
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
194
|
+
patchStepOption(draftStepOptions, index, 'agentVariantId', agentVariantId || undefined)
|
|
163
195
|
}
|
|
164
196
|
|
|
165
197
|
/**
|
|
@@ -188,17 +220,16 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
188
220
|
* wrong thing.
|
|
189
221
|
*/
|
|
190
222
|
function setDraftBinaryOutput(index: number, config: BinaryOutputConfig | undefined) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
223
|
+
const { storageServiceId, contextServiceIds, generatorIds, modalities } = config ?? {}
|
|
224
|
+
const selection = storageServiceId
|
|
225
|
+
? {
|
|
226
|
+
storageServiceId,
|
|
227
|
+
...(contextServiceIds?.length ? { contextServiceIds } : {}),
|
|
228
|
+
...(generatorIds?.length ? { generatorIds } : {}),
|
|
229
|
+
...(modalities?.length ? { modalities } : {}),
|
|
230
|
+
}
|
|
231
|
+
: undefined
|
|
232
|
+
patchStepOption(draftStepOptions, index, 'binaryOutput', selection)
|
|
202
233
|
}
|
|
203
234
|
|
|
204
235
|
/**
|
|
@@ -216,10 +247,7 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
216
247
|
* other fields here.
|
|
217
248
|
*/
|
|
218
249
|
function setDraftMaxOutputTokens(index: number, maxOutputTokens: number | undefined) {
|
|
219
|
-
|
|
220
|
-
if (maxOutputTokens != null) next.maxOutputTokens = maxOutputTokens
|
|
221
|
-
else delete next.maxOutputTokens
|
|
222
|
-
draftStepOptions.value[index] = Object.keys(next).length ? next : null
|
|
250
|
+
patchStepOption(draftStepOptions, index, 'maxOutputTokens', maxOutputTokens ?? undefined)
|
|
223
251
|
}
|
|
224
252
|
|
|
225
253
|
return {
|
|
@@ -234,6 +262,8 @@ export function createPipelineStepConfigActions(ctx: PipelinesContext) {
|
|
|
234
262
|
toggleDraftEnabled,
|
|
235
263
|
draftAutoRecommendEnabled,
|
|
236
264
|
toggleDraftAutoRecommend,
|
|
265
|
+
draftRetainEnvironment,
|
|
266
|
+
toggleDraftRetainEnvironment,
|
|
237
267
|
draftSkillId,
|
|
238
268
|
setDraftSkillId,
|
|
239
269
|
draftAgentVariantId,
|
package/app/types/domain.ts
CHANGED
|
@@ -117,7 +117,7 @@ export type {
|
|
|
117
117
|
PreviewStatus,
|
|
118
118
|
} from '@cat-factory/contracts'
|
|
119
119
|
|
|
120
|
-
import type { AgentCategory, AgentKind, AgentTier } from '@cat-factory/contracts'
|
|
120
|
+
import type { AgentCategory, AgentKind, AgentTier, PipelinePurpose } from '@cat-factory/contracts'
|
|
121
121
|
|
|
122
122
|
// The document-kind list + the per-kind field descriptors are runtime values (used to render
|
|
123
123
|
// the picker and the conditional per-kind inputs), so they are re-exported as values — the
|
|
@@ -135,6 +135,12 @@ export interface AgentArchetype {
|
|
|
135
135
|
description: string
|
|
136
136
|
/** Palette category this archetype is grouped under. Absent ⇒ ungrouped/system kind. */
|
|
137
137
|
category?: AgentCategory
|
|
138
|
+
/**
|
|
139
|
+
* The pipeline PURPOSES the palette offers this kind to, WITHIN the ones its {@link category}
|
|
140
|
+
* already admits (`purposeSuggestsAgentKind`). Absent ⇒ the category alone decides, which is
|
|
141
|
+
* the normal case: declare this only to opt OUT of a purpose the category would admit.
|
|
142
|
+
*/
|
|
143
|
+
purposes?: readonly PipelinePurpose[]
|
|
138
144
|
/**
|
|
139
145
|
* How specialist this kind is — the tier the palette / model-preset override list filter on
|
|
140
146
|
* (`basic` shows only basic kinds, `intermediate` adds those, `advanced` shows everything).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { PipelinePurpose } from '@cat-factory/contracts'
|
|
2
3
|
import type { AgentArchetype } from '~/types/domain'
|
|
3
4
|
import { groupAgentPalette, narrowAgentPalette } from '~/utils/agentPalette'
|
|
4
5
|
|
|
@@ -6,6 +7,7 @@ const archetype = (
|
|
|
6
7
|
kind: string,
|
|
7
8
|
category?: AgentArchetype['category'],
|
|
8
9
|
tier?: AgentArchetype['tier'],
|
|
10
|
+
purposes?: AgentArchetype['purposes'],
|
|
9
11
|
): AgentArchetype => ({
|
|
10
12
|
kind: kind as AgentArchetype['kind'],
|
|
11
13
|
label: kind,
|
|
@@ -14,6 +16,7 @@ const archetype = (
|
|
|
14
16
|
description: kind,
|
|
15
17
|
...(category ? { category } : {}),
|
|
16
18
|
...(tier ? { tier } : {}),
|
|
19
|
+
...(purposes ? { purposes } : {}),
|
|
17
20
|
})
|
|
18
21
|
|
|
19
22
|
// Spread across both dials on purpose: every combination of relevant/irrelevant to a `planning`
|
|
@@ -100,6 +103,33 @@ describe('narrowAgentPalette', () => {
|
|
|
100
103
|
expect(hiddenByBoth).toBe(1)
|
|
101
104
|
})
|
|
102
105
|
|
|
106
|
+
it('narrows WITHIN a category when a kind declares the purposes it is for', () => {
|
|
107
|
+
// The reason relevance is asked of the KIND: a category is a shelf label, so `documenter` and
|
|
108
|
+
// `doc-reviewer` sit on the same shelf while only one of them belongs in a pipeline that
|
|
109
|
+
// reviews someone else's pull request. Both dials still apply to the declaring kind.
|
|
110
|
+
const catalog = [
|
|
111
|
+
archetype('documenter', 'docs', 'basic', ['build', 'document']),
|
|
112
|
+
archetype('doc-reviewer', 'docs', 'basic', ['build', 'review']),
|
|
113
|
+
archetype('house-style', 'docs', 'basic'),
|
|
114
|
+
]
|
|
115
|
+
const offered = (purpose: PipelinePurpose) =>
|
|
116
|
+
narrowAgentPalette(catalog, purpose, 'advanced').offered.map((a) => a.kind)
|
|
117
|
+
expect(offered('review')).toEqual(['doc-reviewer', 'house-style'])
|
|
118
|
+
expect(offered('document')).toEqual(['documenter', 'house-style'])
|
|
119
|
+
// `planning` drops the whole `docs` category, and the declarations do not override that for
|
|
120
|
+
// the kinds that named it: a declared list is the kind's own answer, not an exemption.
|
|
121
|
+
expect(offered('planning')).toEqual([])
|
|
122
|
+
expect(narrowAgentPalette(catalog, 'review', 'advanced').hiddenByPurpose).toBe(1)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('falls back to the category when a declared list names nothing this build knows', () => {
|
|
126
|
+
// The mirror of the unknown-purpose rule: a kind whose entire list was retired has told this
|
|
127
|
+
// build nothing, so its section decides rather than the kind vanishing from every palette.
|
|
128
|
+
const stale = archetype('acme-doc', 'docs', 'basic', ['acme-migration' as never])
|
|
129
|
+
expect(narrowAgentPalette([stale], 'document', 'advanced').offered).toEqual([stale])
|
|
130
|
+
expect(narrowAgentPalette([stale], 'planning', 'advanced').offered).toEqual([])
|
|
131
|
+
})
|
|
132
|
+
|
|
103
133
|
it('keeps a purpose this build does not recognise from narrowing anything', () => {
|
|
104
134
|
// A stored `purpose` from a build that shipped a member this one has not (or has retired):
|
|
105
135
|
// unknown is not a licence to guess, so the palette offers what the tier admits and says so.
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
type AgentTier,
|
|
3
3
|
agentTierVisibleAt,
|
|
4
4
|
type PipelinePurpose,
|
|
5
|
-
|
|
5
|
+
purposeSuggestsAgentKind,
|
|
6
6
|
} from '@cat-factory/contracts'
|
|
7
7
|
import type { AgentArchetype } from '~/types/domain'
|
|
8
8
|
|
|
@@ -36,16 +36,17 @@ export interface NarrowedAgentPalette<T> {
|
|
|
36
36
|
* Reduce `archetypes` to what the palette offers at `purpose` + `tier`, with each dial's hint
|
|
37
37
|
* count (see {@link NarrowedAgentPalette} for what the counts promise).
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* that
|
|
39
|
+
* Relevance is asked of the KIND (`purposeSuggestsAgentKind`), which reads the kind's own
|
|
40
|
+
* `purposes` when it declares one and falls back to its `category`'s row otherwise, so a kind
|
|
41
|
+
* that belongs to one use-case can leave a section its siblings still belong in. An archetype
|
|
42
|
+
* carrying neither has nothing for the purpose dial to judge and is always relevant; an absent
|
|
43
|
+
* `tier` is `DEFAULT_AGENT_TIER`. All three are how a deployment-registered kind that declares
|
|
44
|
+
* nothing behaves, and none of them is a reason to drop it from the catalog.
|
|
42
45
|
*/
|
|
43
|
-
export function narrowAgentPalette<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
): NarrowedAgentPalette<T> {
|
|
48
|
-
const relevant = (a: T) => !a.category || purposeSuggestsAgentCategory(purpose, a.category)
|
|
46
|
+
export function narrowAgentPalette<
|
|
47
|
+
T extends Pick<AgentArchetype, 'tier' | 'category' | 'purposes'>,
|
|
48
|
+
>(archetypes: readonly T[], purpose: PipelinePurpose, tier: AgentTier): NarrowedAgentPalette<T> {
|
|
49
|
+
const relevant = (a: T) => purposeSuggestsAgentKind(purpose, a)
|
|
49
50
|
const inTier = (a: T) => agentTierVisibleAt(a.tier, tier)
|
|
50
51
|
return {
|
|
51
52
|
offered: archetypes.filter((a) => relevant(a) && inTier(a)),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { PIPELINE_PURPOSES, purposeAllowsAgentCategory } from '@cat-factory/contracts'
|
|
2
3
|
import type { AgentKind, BlockStatus, BlockType } from '~/types/domain'
|
|
4
|
+
import { narrowAgentPalette } from '~/utils/agentPalette'
|
|
3
5
|
import {
|
|
4
6
|
AGENT_ARCHETYPES,
|
|
5
7
|
AGENT_BY_KIND,
|
|
@@ -85,6 +87,39 @@ describe('catalog', () => {
|
|
|
85
87
|
expect(basic).toEqual(expect.arrayContaining(['architect', 'coder', 'tester-api']))
|
|
86
88
|
})
|
|
87
89
|
|
|
90
|
+
it('leaves every purpose a palette to build from, and every declaration saveable', () => {
|
|
91
|
+
// Two properties over the whole grid rather than a pinned count, which every ordinary
|
|
92
|
+
// addition would break without naming anything.
|
|
93
|
+
//
|
|
94
|
+
// A `purposes` declaration only ever HIDES, so the way to get it wrong is to hide too much:
|
|
95
|
+
// a purpose whose palette reduces to nothing is a dial setting with no way forward, and the
|
|
96
|
+
// widest tier is where that has to be checked because the tier hint is the way out of a thin
|
|
97
|
+
// one. And relevance stays a subset of compatibility per KIND, so a declaration can never
|
|
98
|
+
// offer a step the save gate would then refuse.
|
|
99
|
+
for (const purpose of PIPELINE_PURPOSES) {
|
|
100
|
+
const offered = narrowAgentPalette(AGENT_ARCHETYPES, purpose, 'advanced').offered
|
|
101
|
+
expect(offered.length, `${purpose} offers no agent at all`).toBeGreaterThan(0)
|
|
102
|
+
for (const a of offered) {
|
|
103
|
+
expect(
|
|
104
|
+
!a.category || purposeAllowsAgentCategory(purpose, a.category),
|
|
105
|
+
`${a.kind} is offered to a ${purpose} pipeline its step could not be saved in`,
|
|
106
|
+
).toBe(true)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('never declares an EMPTY `purposes` (which reads as no declaration, not as "nowhere")', () => {
|
|
112
|
+
// `agentPresentationSchema` refuses an empty list at registration, but this catalog is
|
|
113
|
+
// authored in TypeScript and parsed by nothing, so the same guard has to be asserted for the
|
|
114
|
+
// half valibot never sees. Left empty, a kind someone meant to offer NOWHERE is offered
|
|
115
|
+
// everywhere its section is: `purposeSuggestsAgentKind` cannot tell an authored `[]` from a
|
|
116
|
+
// kind that declared nothing at all.
|
|
117
|
+
for (const a of [...AGENT_ARCHETYPES, ...Object.values(SYSTEM_AGENT_META)]) {
|
|
118
|
+
if (!a.purposes) continue
|
|
119
|
+
expect(a.purposes.length, `${a.kind} declares an empty purposes list`).toBeGreaterThan(0)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
88
123
|
it('never shadows a companion producer as a system kind', () => {
|
|
89
124
|
// A companion is never placed directly: the builder renders it as a toggle on its producer
|
|
90
125
|
// step, so a producer that cannot be placed takes its companion out of the builder with it.
|
package/app/utils/catalog.ts
CHANGED
|
@@ -35,6 +35,10 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
35
35
|
icon: 'i-lucide-clipboard-check',
|
|
36
36
|
color: '#f59e0b',
|
|
37
37
|
category: 'review',
|
|
38
|
+
// Settles the PRODUCT layer before anyone builds, which is every use-case except reviewing
|
|
39
|
+
// someone else's open pull request: there the requirements are already someone's shipped
|
|
40
|
+
// decision and nothing here can change them.
|
|
41
|
+
purposes: ['build', 'document', 'research', 'planning'],
|
|
38
42
|
description:
|
|
39
43
|
'Reviews the collected context (description + linked PRDs/RFCs) for gaps, ambiguities, assumptions and risks before the architect starts.',
|
|
40
44
|
// Opens the dedicated structured review window (answer/dismiss findings → incorporate
|
|
@@ -48,6 +52,8 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
48
52
|
icon: 'i-lucide-bug',
|
|
49
53
|
color: '#f59e0b',
|
|
50
54
|
category: 'review',
|
|
55
|
+
// Triages a BUG REPORT for fixability, so it only makes sense where something gets fixed.
|
|
56
|
+
purposes: ['build'],
|
|
51
57
|
description:
|
|
52
58
|
'Triages a bug report for fixability — raising questions, gaps and assumptions about the report before anyone starts fixing it.',
|
|
53
59
|
// Opens the dedicated structured review window (answer/dismiss findings → incorporate
|
|
@@ -65,6 +71,9 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
65
71
|
icon: 'i-lucide-search-code',
|
|
66
72
|
color: '#38bdf8',
|
|
67
73
|
category: 'review',
|
|
74
|
+
// Traces a bug to its root cause in the code: a fixing pipeline's opening move, and nothing
|
|
75
|
+
// a document, review, spike or plan has any use for.
|
|
76
|
+
purposes: ['build'],
|
|
68
77
|
description:
|
|
69
78
|
'Read-only, multi-repo codebase investigation that traces the bug to its root cause and decides whether the report is fixable as-is or needs the reporter to clarify (no code changes).',
|
|
70
79
|
resultView: 'generic-structured',
|
|
@@ -81,6 +90,9 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
81
90
|
icon: 'i-lucide-clipboard-check',
|
|
82
91
|
color: '#6366f1',
|
|
83
92
|
category: 'review',
|
|
93
|
+
// Reviews an EXISTING open pull request, which is the whole of the review use-case, and is
|
|
94
|
+
// available to a build pipeline that wants a deep pass over the pull request it just opened.
|
|
95
|
+
purposes: ['build', 'review'],
|
|
84
96
|
description:
|
|
85
97
|
'Deep, token-bounded review of an open pull request: slices a large diff into cohesive ' +
|
|
86
98
|
'chunks, reviews each, and returns prioritized findings.',
|
|
@@ -148,6 +160,8 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
148
160
|
icon: 'i-lucide-clipboard-list',
|
|
149
161
|
color: '#c084fc',
|
|
150
162
|
category: 'design',
|
|
163
|
+
// Writes the in-repo spec the implementation is then built against.
|
|
164
|
+
purposes: ['build', 'planning'],
|
|
151
165
|
description:
|
|
152
166
|
"Aggregates every task's clarified requirements into the service's in-repo specification (spec.json) with full acceptance-scenario coverage, derived into Gherkin.",
|
|
153
167
|
},
|
|
@@ -158,6 +172,9 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
158
172
|
icon: 'i-lucide-drafting-compass',
|
|
159
173
|
color: '#a78bfa',
|
|
160
174
|
category: 'design',
|
|
175
|
+
// Designs the shape of a CODE change, so it belongs wherever code is planned or written and
|
|
176
|
+
// nowhere a document is being authored.
|
|
177
|
+
purposes: ['build', 'research', 'planning'],
|
|
161
178
|
description: 'Designs the shape of the solution and breaks down the work.',
|
|
162
179
|
},
|
|
163
180
|
{
|
|
@@ -170,6 +187,8 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
170
187
|
icon: 'i-lucide-map',
|
|
171
188
|
color: '#22d3ee',
|
|
172
189
|
category: 'design',
|
|
190
|
+
// Decomposes a repository into services and modules on the board.
|
|
191
|
+
purposes: ['build', 'planning'],
|
|
173
192
|
description: 'Maps the repository into the service → modules blueprint.',
|
|
174
193
|
},
|
|
175
194
|
{
|
|
@@ -210,12 +229,14 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
210
229
|
},
|
|
211
230
|
{
|
|
212
231
|
// Provisions the ephemeral environment the tester / human-test / playwright steps read, which
|
|
213
|
-
// is why it leads the testing group.
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
232
|
+
// is why it leads the testing group.
|
|
233
|
+
//
|
|
234
|
+
// `basic`, and it has to be: a pipeline that reaches an env consumer with no Deployer in
|
|
235
|
+
// front of it is refused at SAVE (`validatePipelineAuthoring`), and the API Tester it serves
|
|
236
|
+
// is itself `basic`. Leaving the Deployer out of the basic palette would leave a basic-mode
|
|
237
|
+
// user composing a pipeline they cannot save and cannot see the fix for.
|
|
217
238
|
kind: 'deployer',
|
|
218
|
-
tier: '
|
|
239
|
+
tier: 'basic',
|
|
219
240
|
label: 'Deployer',
|
|
220
241
|
icon: 'i-lucide-cloud-upload',
|
|
221
242
|
color: '#34d399',
|
|
@@ -276,8 +297,11 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
276
297
|
// is the point of it: after the automated tester, or after a human has finished with the live
|
|
277
298
|
// URL. Without one, the TTL sweep reclaims environments on a timer long after the run
|
|
278
299
|
// settled, which is a fine backstop and cannot close the run's own teardown proof.
|
|
300
|
+
//
|
|
301
|
+
// `basic` for the same reason the Deployer is: a chain that deploys and never reclaims is
|
|
302
|
+
// refused at save, so the fix has to be reachable wherever the fault can be composed.
|
|
279
303
|
kind: 'disposer',
|
|
280
|
-
tier: '
|
|
304
|
+
tier: 'basic',
|
|
281
305
|
label: 'Disposer',
|
|
282
306
|
icon: 'i-lucide-cloud-off',
|
|
283
307
|
color: '#34d399',
|
|
@@ -305,6 +329,9 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
305
329
|
icon: 'i-lucide-book-open-text',
|
|
306
330
|
color: '#818cf8',
|
|
307
331
|
category: 'docs',
|
|
332
|
+
// WRITES documentation into the repository, which a pipeline that reviews someone else's
|
|
333
|
+
// pull request never does.
|
|
334
|
+
purposes: ['build', 'document'],
|
|
308
335
|
description: 'Produces docs and usage examples.',
|
|
309
336
|
},
|
|
310
337
|
{
|
|
@@ -314,6 +341,8 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
314
341
|
icon: 'i-lucide-scroll-text',
|
|
315
342
|
color: '#84cc16',
|
|
316
343
|
category: 'docs',
|
|
344
|
+
// Writes domain-rule docs into the repository (see `documenter`).
|
|
345
|
+
purposes: ['build', 'document'],
|
|
317
346
|
description:
|
|
318
347
|
'Reads the implementation and writes/updates business-logic & domain-rule docs in the repo, weaving in linked context documents.',
|
|
319
348
|
},
|
|
@@ -324,6 +353,9 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
324
353
|
icon: 'i-lucide-shield-alert',
|
|
325
354
|
color: '#ef4444',
|
|
326
355
|
category: 'docs',
|
|
356
|
+
// The review activity that groups under Documentation: it reads a change against the
|
|
357
|
+
// documented rules and reports violations, writing nothing.
|
|
358
|
+
purposes: ['build', 'review'],
|
|
327
359
|
description:
|
|
328
360
|
'Reviews a change against the documented domain rules and reports violations, undocumented changes and unexpected drift.',
|
|
329
361
|
},
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
pipelineAllowedForTaskType,
|
|
9
9
|
purposeAllowsAgentCategory,
|
|
10
10
|
purposeSuggestsAgentCategory,
|
|
11
|
+
purposeSuggestsAgentKind,
|
|
11
12
|
} from '@cat-factory/contracts'
|
|
13
|
+
import type { PipelinePurpose } from '@cat-factory/contracts'
|
|
12
14
|
import type { Block, Pipeline } from '~/types/domain'
|
|
13
15
|
import {
|
|
14
16
|
pipelineAllowedForManualStart,
|
|
@@ -216,6 +218,73 @@ describe('purposeSuggestsAgentCategory (builder palette filter)', () => {
|
|
|
216
218
|
})
|
|
217
219
|
})
|
|
218
220
|
|
|
221
|
+
describe('purposeSuggestsAgentKind (what the palette actually filters on)', () => {
|
|
222
|
+
it('defers to the category for a kind that declares no purposes of its own', () => {
|
|
223
|
+
// The normal case, and the one that keeps a deployment-registered kind exactly as visible as
|
|
224
|
+
// it was before kinds could speak for themselves.
|
|
225
|
+
for (const purpose of [...PIPELINE_PURPOSES, UNKNOWN_PURPOSE]) {
|
|
226
|
+
for (const category of AGENT_CATEGORIES) {
|
|
227
|
+
expect(purposeSuggestsAgentKind(purpose, { category })).toBe(
|
|
228
|
+
purposeSuggestsAgentCategory(purpose, category),
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
expect(purposeSuggestsAgentKind(purpose, {})).toBe(true)
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('lets a kind narrow within a category its siblings still belong to', () => {
|
|
236
|
+
// What the category table structurally cannot say: `docs` survives a `review` pipeline so the
|
|
237
|
+
// Domain Rules Reviewer does, which also handed it the two kinds that WRITE documentation.
|
|
238
|
+
const author = { category: 'docs', purposes: ['build', 'document'] } as const
|
|
239
|
+
expect(purposeSuggestsAgentKind('document', author)).toBe(true)
|
|
240
|
+
expect(purposeSuggestsAgentKind('review', author)).toBe(false)
|
|
241
|
+
expect(purposeSuggestsAgentCategory('review', 'docs')).toBe(true)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('is a declaration, not an exemption from the category', () => {
|
|
245
|
+
// A kind cannot buy its way back into a purpose its section is not offered to: `docs` is gone
|
|
246
|
+
// for `planning`, and the palette drops the kind whether or not it named `planning` itself.
|
|
247
|
+
const doc = { category: 'docs', purposes: ['planning'] } as const
|
|
248
|
+
expect(purposeSuggestsAgentKind('planning', doc)).toBe(
|
|
249
|
+
purposeSuggestsAgentCategory('planning', 'docs'),
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('reads a list naming nothing this build knows as no declaration at all', () => {
|
|
254
|
+
const stale = { category: 'docs', purposes: [UNKNOWN_PURPOSE] } as const
|
|
255
|
+
for (const purpose of PIPELINE_PURPOSES) {
|
|
256
|
+
expect(purposeSuggestsAgentKind(purpose, stale)).toBe(
|
|
257
|
+
purposeSuggestsAgentCategory(purpose, 'docs'),
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
// And a purpose this build cannot name still narrows nothing, declaration or not.
|
|
261
|
+
expect(
|
|
262
|
+
purposeSuggestsAgentKind(UNKNOWN_PURPOSE, { category: 'docs', purposes: ['build'] }),
|
|
263
|
+
).toBe(true)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('never offers what the save gate would refuse', () => {
|
|
267
|
+
// The same invariant the category predicate carries, restated where the palette now reads it:
|
|
268
|
+
// a kind's own declaration may only ever hide more, so it can never open a hole through which
|
|
269
|
+
// a kind is offered and its step then blocks the save.
|
|
270
|
+
for (const purpose of [...PIPELINE_PURPOSES, UNKNOWN_PURPOSE]) {
|
|
271
|
+
for (const category of AGENT_CATEGORIES) {
|
|
272
|
+
const declarations: (readonly PipelinePurpose[] | undefined)[] = [
|
|
273
|
+
undefined,
|
|
274
|
+
PIPELINE_PURPOSES,
|
|
275
|
+
['build'],
|
|
276
|
+
[UNKNOWN_PURPOSE],
|
|
277
|
+
]
|
|
278
|
+
for (const purposes of declarations) {
|
|
279
|
+
if (purposeSuggestsAgentKind(purpose, { category, purposes })) {
|
|
280
|
+
expect(purposeAllowsAgentCategory(purpose, category)).toBe(true)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
|
|
219
288
|
describe('a purpose or category this build does not recognise', () => {
|
|
220
289
|
it('is recognised as unknown rather than trusted', () => {
|
|
221
290
|
for (const purpose of PIPELINE_PURPOSES) expect(isPipelinePurpose(purpose)).toBe(true)
|
package/i18n/locales/de.json
CHANGED
|
@@ -4293,6 +4293,13 @@
|
|
|
4293
4293
|
"binaryOutputPlaceholder": "Speicherdienst wählen",
|
|
4294
4294
|
"binaryOutputContextPlaceholder": "Optional: Dienste, die den Umfang bestimmen",
|
|
4295
4295
|
"binaryOutputNeedsPick": "Für einen Schritt, der binäre Ausgaben erzeugt, ist kein Speicherdienst gewählt. Wähle einen vor dem Speichern.",
|
|
4296
|
+
"envNeedsDeployer": "Vor einem Tester-, manuellen Test- oder Playwright-Schritt wird ein Deployer benötigt. Füge einen hinzu, sonst lässt sich die Pipeline nicht speichern (bei einem Service ohne Provisionierung bleibt er wirkungslos).",
|
|
4297
|
+
"envNeedsDisposer": "Nach einem Deployer wird ein Disposer benötigt, der die bereitgestellte Umgebung wieder freigibt. Füge einen hinzu oder markiere den Deployer so, dass er seine Umgebung über den Lauf hinaus behält, sonst lässt sich die Pipeline nicht speichern.",
|
|
4298
|
+
"envDisposerNeedsDeployer": "Vor diesem Disposer steht kein Deployer, es gibt also nichts freizugeben. Füge einen Deployer hinzu oder entferne den Disposer.",
|
|
4299
|
+
"envConsumerAfterDisposer": "Ein Tester-, manueller Test- oder Playwright-Schritt läuft erst, nachdem der Disposer die Umgebung bereits freigegeben hat, es bliebe also nichts übrig, wogegen er laufen könnte. Verschiebe den Disposer hinter diesen Schritt oder füge davor einen weiteren Deployer ein.",
|
|
4300
|
+
"envRetainedButReclaimed": "Dieser Deployer ist so markiert, dass er seine Umgebung über den Lauf hinaus behält, aber ein nachfolgender Disposer gibt genau diese Umgebung frei. Entferne den Disposer oder hebe die Markierung auf.",
|
|
4301
|
+
"retainEnvironmentSetTooltip": "Wird am Ende des Laufs freigegeben. Klicke, um diese Umgebung danach weiterlaufen zu lassen (eine Vorschau, die Prüfende nach dem Öffnen des PR nutzen); die TTL oder ein Betreiber fährt sie dann herunter.",
|
|
4302
|
+
"retainEnvironmentClearTooltip": "Läuft nach dem Ende des Laufs weiter. Klicke, um sie wieder per Disposer-Schritt freizugeben.",
|
|
4296
4303
|
"binaryOutputNoStorage": "Kein Dienst im Katalog dieses Boards deklariert die Fähigkeit {capability}. Registriere einen, oder ergänze die Fähigkeit beim gemeinten Dienst unter den grundlegenden Diensten.",
|
|
4297
4304
|
"binaryOutputMissing": "Dieser Speicherdienst ist nicht mehr im Katalog; wähle einen anderen.",
|
|
4298
4305
|
"binaryOutputNotStorage": "Dieser Dienst deklariert die Fähigkeit {capability} nicht mehr, deshalb werden Läufe abgelehnt; wähle einen anderen.",
|
package/i18n/locales/en.json
CHANGED
|
@@ -4881,6 +4881,13 @@
|
|
|
4881
4881
|
"binaryOutputPlaceholder": "Pick a storage service",
|
|
4882
4882
|
"binaryOutputContextPlaceholder": "Optional: services that scope the generation",
|
|
4883
4883
|
"binaryOutputNeedsPick": "A step that generates binary outputs has no storage service selected. Pick one before saving.",
|
|
4884
|
+
"envNeedsDeployer": "A Tester, human-test or Playwright step needs a Deployer before it. Add one or the pipeline won't save (it is a no-op on a service that provisions nothing).",
|
|
4885
|
+
"envNeedsDisposer": "A Deployer needs a Disposer after it to reclaim the environment it stands up. Add one, or mark the Deployer as keeping its environment past the run, otherwise the pipeline won't save.",
|
|
4886
|
+
"envDisposerNeedsDeployer": "This Disposer has no Deployer before it, so there is nothing for it to reclaim. Add a Deployer, or remove the Disposer.",
|
|
4887
|
+
"envConsumerAfterDisposer": "A Tester, human-test or Playwright step runs after the Disposer has already reclaimed the environment, so nothing would be left to run against. Move the Disposer below that step, or add another Deployer before it.",
|
|
4888
|
+
"envRetainedButReclaimed": "This Deployer is marked as keeping its environment past the run, but a Disposer after it reclaims exactly that environment. Remove the Disposer, or clear the keep-environment setting.",
|
|
4889
|
+
"retainEnvironmentSetTooltip": "Reclaimed at the end of the run. Click to keep this environment running afterwards (a preview reviewers use once the PR is open); its TTL or an operator then takes it down.",
|
|
4890
|
+
"retainEnvironmentClearTooltip": "Kept running after the run ends. Click to go back to reclaiming it with a Disposer step.",
|
|
4884
4891
|
"binaryOutputNoStorage": "No service in this board's catalog declares the {capability} capability. Register one, or add the capability to the service you meant, under foundational services.",
|
|
4885
4892
|
"binaryOutputMissing": "This storage service is no longer in the catalog; pick another.",
|
|
4886
4893
|
"binaryOutputNotStorage": "This service no longer declares the {capability} capability, so runs will be refused; pick another.",
|
package/i18n/locales/es.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Elige un servicio de almacenamiento",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Opcional: servicios que delimitan la generación",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Un paso que genera salidas binarias no tiene servicio de almacenamiento elegido. Elige uno antes de guardar.",
|
|
4735
|
+
"envNeedsDeployer": "Un paso de Tester, prueba manual o Playwright necesita un Deployer antes. Añade uno o la canalización no se guardará (no hace nada en un servicio que no aprovisiona).",
|
|
4736
|
+
"envNeedsDisposer": "Un Deployer necesita un Disposer después para liberar el entorno que levanta. Añade uno, o marca el Deployer para que conserve su entorno más allá de la ejecución; de lo contrario la canalización no se guardará.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Este Disposer no tiene ningún Deployer antes, así que no hay nada que liberar. Añade un Deployer o quita el Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Un paso de Tester, prueba manual o Playwright se ejecuta después de que el Disposer ya haya liberado el entorno, así que no quedaría nada contra lo que ejecutarlo. Mueve el Disposer por debajo de ese paso, o añade otro Deployer antes.",
|
|
4739
|
+
"envRetainedButReclaimed": "Este Deployer está marcado para conservar su entorno más allá de la ejecución, pero un Disposer posterior libera exactamente ese entorno. Quita el Disposer o desmarca la opción de conservar el entorno.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Se libera al terminar la ejecución. Haz clic para mantener este entorno en marcha después (una vista previa que los revisores usan con el PR abierto); luego lo retirará su TTL o un operador.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Se mantiene en marcha tras la ejecución. Haz clic para volver a liberarlo con un paso Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Ningún servicio del catálogo de este tablero declara la capacidad {capability}. Registra uno, o añade la capacidad al servicio que tenías en mente, en servicios fundamentales.",
|
|
4736
4743
|
"binaryOutputMissing": "Este servicio de almacenamiento ya no está en el catálogo; elige otro.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Este servicio ya no declara la capacidad {capability}, así que las ejecuciones se rechazarán; elige otro.",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "Choisir un service de stockage",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "Facultatif : services qui délimitent la génération",
|
|
4734
4734
|
"binaryOutputNeedsPick": "Une étape qui génère des sorties binaires n'a aucun service de stockage choisi. Choisissez-en un avant d'enregistrer.",
|
|
4735
|
+
"envNeedsDeployer": "Une étape Tester, de test manuel ou Playwright a besoin d'un Deployer avant elle. Ajoutez-en un, sinon le pipeline ne sera pas enregistré (il ne fait rien sur un service qui ne provisionne rien).",
|
|
4736
|
+
"envNeedsDisposer": "Un Deployer a besoin d'un Disposer après lui pour libérer l'environnement qu'il provisionne. Ajoutez-en un, ou marquez le Deployer comme conservant son environnement au-delà de l'exécution, sinon le pipeline ne sera pas enregistré.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "Ce Disposer n'a aucun Deployer avant lui, il n'a donc rien à libérer. Ajoutez un Deployer ou retirez le Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "Une étape Tester, de test manuel ou Playwright s'exécute après que le Disposer a déjà libéré l'environnement : il ne resterait rien pour l'exécuter. Déplacez le Disposer après cette étape, ou ajoutez un autre Deployer avant elle.",
|
|
4739
|
+
"envRetainedButReclaimed": "Ce Deployer est marqué comme conservant son environnement au-delà de l'exécution, mais un Disposer placé après lui libère précisément cet environnement. Retirez le Disposer, ou désactivez la conservation de l'environnement.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "Libéré à la fin de l'exécution. Cliquez pour laisser cet environnement actif ensuite (un aperçu que les relecteurs utilisent une fois la PR ouverte) ; son TTL ou un opérateur l'arrêtera.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "Reste actif après la fin de l'exécution. Cliquez pour le libérer de nouveau via une étape Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "Aucun service du catalogue de ce tableau ne déclare la capacité {capability}. Enregistrez-en un, ou ajoutez la capacité au service que vous visiez, dans les services fondamentaux.",
|
|
4736
4743
|
"binaryOutputMissing": "Ce service de stockage n'est plus dans le catalogue ; choisissez-en un autre.",
|
|
4737
4744
|
"binaryOutputNotStorage": "Ce service ne déclare plus la capacité {capability}, les exécutions seront donc refusées ; choisissez-en un autre.",
|
package/i18n/locales/he.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "בחר שירות אחסון",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "לא חובה: שירותים שמגדירים את היקף היצירה",
|
|
4734
4734
|
"binaryOutputNeedsPick": "לשלב שמייצר פלטים בינאריים לא נבחר שירות אחסון. בחר אחד לפני השמירה.",
|
|
4735
|
+
"envNeedsDeployer": "שלב Tester, בדיקה ידנית או Playwright דורש Deployer לפניו. הוסף אחד, אחרת הצינור לא יישמר (בשירות שאינו מקצה סביבה הוא לא עושה דבר).",
|
|
4736
|
+
"envNeedsDisposer": "אחרי Deployer נדרש Disposer שישחרר את הסביבה שהוקצתה. הוסף אחד, או סמן את ה-Deployer כשומר על הסביבה שלו גם אחרי הריצה, אחרת הצינור לא יישמר.",
|
|
4737
|
+
"envDisposerNeedsDeployer": "לפני ה-Disposer הזה אין Deployer, ולכן אין מה לשחרר. הוסף Deployer או הסר את ה-Disposer.",
|
|
4738
|
+
"envConsumerAfterDisposer": "שלב Tester, בדיקה ידנית או Playwright רץ אחרי שה-Disposer כבר שחרר את הסביבה, ולכן לא יישאר דבר להריץ מולו. העבר את ה-Disposer מתחת לשלב הזה, או הוסף Deployer נוסף לפניו.",
|
|
4739
|
+
"envRetainedButReclaimed": "ה-Deployer הזה מסומן כשומר על הסביבה שלו גם אחרי הריצה, אך Disposer שאחריו משחרר בדיוק את אותה סביבה. הסר את ה-Disposer, או בטל את סימון שמירת הסביבה.",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "משוחררת בסוף הריצה. לחץ כדי להשאיר את הסביבה פעילה אחריה (תצוגה מקדימה שסוקרים משתמשים בה אחרי פתיחת ה-PR); ה-TTL או מפעיל יורידו אותה בהמשך.",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "נשארת פעילה אחרי סיום הריצה. לחץ כדי לחזור לשחרור שלה בשלב Disposer.",
|
|
4735
4742
|
"binaryOutputNoStorage": "אף שירות בקטלוג של לוח זה אינו מצהיר על יכולת {capability}. רשום שירות כזה, או הוסף את היכולת לשירות שהתכוונת אליו, תחת שירותי בסיס.",
|
|
4736
4743
|
"binaryOutputMissing": "שירות אחסון זה כבר אינו בקטלוג; בחר אחר.",
|
|
4737
4744
|
"binaryOutputNotStorage": "השירות הזה כבר אינו מצהיר על יכולת {capability}, ולכן הרצות יידחו; בחר אחר.",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4293,6 +4293,13 @@
|
|
|
4293
4293
|
"binaryOutputPlaceholder": "Scegli un servizio di archiviazione",
|
|
4294
4294
|
"binaryOutputContextPlaceholder": "Facoltativo: servizi che delimitano la generazione",
|
|
4295
4295
|
"binaryOutputNeedsPick": "Un passo che genera output binari non ha un servizio di archiviazione scelto. Scegline uno prima di salvare.",
|
|
4296
|
+
"envNeedsDeployer": "Un passaggio Tester, di test manuale o Playwright ha bisogno di un Deployer prima di sé. Aggiungine uno, altrimenti la pipeline non verrà salvata (su un servizio che non effettua provisioning non fa nulla).",
|
|
4297
|
+
"envNeedsDisposer": "Un Deployer ha bisogno di un Disposer dopo di sé, che liberi l'ambiente creato. Aggiungine uno, oppure contrassegna il Deployer come tale da mantenere il proprio ambiente oltre l'esecuzione, altrimenti la pipeline non verrà salvata.",
|
|
4298
|
+
"envDisposerNeedsDeployer": "Questo Disposer non ha alcun Deployer prima di sé, quindi non ha nulla da liberare. Aggiungi un Deployer oppure rimuovi il Disposer.",
|
|
4299
|
+
"envConsumerAfterDisposer": "Un passaggio Tester, di test manuale o Playwright viene eseguito dopo che il Disposer ha già liberato l'ambiente, quindi non resterebbe nulla su cui eseguirlo. Sposta il Disposer dopo quel passaggio, oppure aggiungi un altro Deployer prima di esso.",
|
|
4300
|
+
"envRetainedButReclaimed": "Questo Deployer è contrassegnato come tale da mantenere il proprio ambiente oltre l'esecuzione, ma un Disposer successivo libera proprio quell'ambiente. Rimuovi il Disposer, oppure togli il contrassegno di mantenimento dell'ambiente.",
|
|
4301
|
+
"retainEnvironmentSetTooltip": "Liberato al termine dell'esecuzione. Fai clic per lasciare questo ambiente attivo dopo (un'anteprima che i revisori usano a PR aperta); lo spegneranno poi il suo TTL o un operatore.",
|
|
4302
|
+
"retainEnvironmentClearTooltip": "Resta attivo dopo la fine dell'esecuzione. Fai clic per tornare a liberarlo con un passaggio Disposer.",
|
|
4296
4303
|
"binaryOutputNoStorage": "Nessun servizio nel catalogo di questa board dichiara la capacità {capability}. Registrane uno, oppure aggiungi la capacità al servizio che intendevi, nei servizi fondamentali.",
|
|
4297
4304
|
"binaryOutputMissing": "Questo servizio di archiviazione non è più nel catalogo; scegline un altro.",
|
|
4298
4305
|
"binaryOutputNotStorage": "Questo servizio non dichiara più la capacità {capability}, quindi le esecuzioni verranno rifiutate; scegline un altro.",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -4732,6 +4732,13 @@
|
|
|
4732
4732
|
"binaryOutputPlaceholder": "保存サービスを選択",
|
|
4733
4733
|
"binaryOutputContextPlaceholder": "任意: 生成の範囲を定めるサービス",
|
|
4734
4734
|
"binaryOutputNeedsPick": "バイナリ成果物を生成するステップに保存サービスが選択されていません。保存前に選んでください。",
|
|
4735
|
+
"envNeedsDeployer": "Tester、手動テスト、または Playwright のステップの前には Deployer が必要です。追加しないとパイプラインは保存できません(プロビジョニングしないサービスでは何もしません)。",
|
|
4736
|
+
"envNeedsDisposer": "Deployer の後には、用意した環境を解放する Disposer が必要です。追加するか、Deployer に実行後も環境を残す設定を付けてください。どちらもない場合、パイプラインは保存できません。",
|
|
4737
|
+
"envDisposerNeedsDeployer": "この Disposer の前に Deployer がないため、解放する対象がありません。Deployer を追加するか、Disposer を削除してください。",
|
|
4738
|
+
"envConsumerAfterDisposer": "Tester、手動テスト、または Playwright のステップが、Disposer が環境を解放した後に実行されるため、実行対象が残りません。Disposer をそのステップより後ろに移すか、その前にもう一つ Deployer を追加してください。",
|
|
4739
|
+
"envRetainedButReclaimed": "この Deployer には実行後も環境を残す設定が付いていますが、後続の Disposer がまさにその環境を解放します。Disposer を削除するか、環境を残す設定を解除してください。",
|
|
4740
|
+
"retainEnvironmentSetTooltip": "実行の終了時に解放されます。クリックすると実行後も環境を残します(PR を開いた後にレビュアーが使うプレビュー用)。その後は TTL または運用者が停止します。",
|
|
4741
|
+
"retainEnvironmentClearTooltip": "実行の終了後も環境を残します。クリックすると Disposer ステップで解放する動作に戻ります。",
|
|
4735
4742
|
"binaryOutputNoStorage": "このボードのカタログに {capability} 機能を宣言するサービスがありません。基盤サービスで新たに登録するか、意図したサービスにこの機能を追加してください。",
|
|
4736
4743
|
"binaryOutputMissing": "この保存サービスはカタログに存在しません。別のサービスを選んでください。",
|
|
4737
4744
|
"binaryOutputNotStorage": "このサービスは {capability} 機能を宣言しなくなったため、実行は拒否されます。別のサービスを選んでください。",
|