@cat-factory/app 0.234.2 → 0.236.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 +20 -5
- package/app/components/board/AddTaskModal.vue +31 -12
- package/app/components/board/CreateInitiativeModal.vue +33 -6
- package/app/components/context/ContextAttachmentFields.vue +63 -42
- package/app/components/documents/ContextDocumentPicker.logic.spec.ts +186 -0
- package/app/components/documents/ContextDocumentPicker.logic.ts +178 -0
- package/app/components/documents/ContextDocumentPicker.vue +269 -34
- package/app/components/pipeline/AgentKindIcon.vue +1 -1
- package/app/components/settings/ModelConfigurationPanel.vue +2 -2
- package/app/composables/api/documents.ts +10 -0
- package/app/composables/useContextLinking.spec.ts +95 -0
- package/app/composables/useContextLinking.ts +96 -15
- package/app/composables/useWorkspaceStream.ts +31 -77
- package/app/composables/workspaceStream/applyWorkspaceEvent.spec.ts +150 -0
- package/app/composables/workspaceStream/applyWorkspaceEvent.ts +173 -0
- package/app/stores/agents.ts +5 -3
- package/app/stores/documents.ts +12 -0
- package/app/types/documents.ts +2 -0
- package/app/utils/catalog.spec.ts +37 -2
- package/app/utils/catalog.ts +56 -36
- package/i18n/locales/de.json +15 -4
- package/i18n/locales/en.json +19 -2
- package/i18n/locales/es.json +15 -4
- package/i18n/locales/fr.json +15 -4
- package/i18n/locales/he.json +15 -4
- package/i18n/locales/it.json +15 -4
- package/i18n/locales/ja.json +15 -4
- package/i18n/locales/pl.json +15 -4
- package/i18n/locales/tr.json +15 -4
- package/i18n/locales/uk.json +15 -4
- package/package.json +2 -2
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Block,
|
|
3
|
+
BootstrapJob,
|
|
4
|
+
EnvConfigRepairJob,
|
|
5
|
+
EnvironmentTestRun,
|
|
6
|
+
ExecutionInstance,
|
|
7
|
+
InfraSetupArea,
|
|
8
|
+
InfraSetupStatus,
|
|
9
|
+
Initiative,
|
|
10
|
+
KaizenGrading,
|
|
11
|
+
LlmCallActivity,
|
|
12
|
+
Notification,
|
|
13
|
+
WorkspaceEvent,
|
|
14
|
+
} from '~/types/domain'
|
|
15
|
+
import type { BrainstormSession } from '~/types/brainstorm'
|
|
16
|
+
import type { ClarityReview } from '~/types/clarity'
|
|
17
|
+
import type { ConsensusSession } from '~/types/consensus'
|
|
18
|
+
import type { DocInterviewSession } from '~/types/doc-interview'
|
|
19
|
+
import type { RequirementReview } from '~/types/requirements'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Everything {@link applyWorkspaceEvent} needs to route one pushed event into the stores, as bound
|
|
23
|
+
* callbacks rather than the stores themselves: it makes the routing (above all the `board` branch's
|
|
24
|
+
* targeted-vs-coarse decision) unit-testable without a Pinia instance or a live socket.
|
|
25
|
+
*
|
|
26
|
+
* Each callback names the DOMAIN type it takes, not the event field it happens to be fed from
|
|
27
|
+
* today. `upsertBlock` is the reason that matters: three branches (`execution`, `board`,
|
|
28
|
+
* `bootstrap`) share it, so typing it off any one of their payloads would silently retype the
|
|
29
|
+
* other two the next time that event's shape moved.
|
|
30
|
+
*/
|
|
31
|
+
export interface WorkspaceEventTargets {
|
|
32
|
+
upsertExecution: (instance: ExecutionInstance) => void
|
|
33
|
+
upsertBlock: (block: Block) => void
|
|
34
|
+
upsertBootstrap: (job: BootstrapJob) => void
|
|
35
|
+
upsertEnvConfigRepair: (job: EnvConfigRepairJob) => void
|
|
36
|
+
upsertEnvironmentTest: (run: EnvironmentTestRun) => void
|
|
37
|
+
patchInfraSetup: (area: InfraSetupArea, status: InfraSetupStatus, detail?: string) => void
|
|
38
|
+
upsertNotification: (n: Notification) => void
|
|
39
|
+
appendLlmCall: (call: LlmCallActivity) => void
|
|
40
|
+
upsertRequirements: (r: RequirementReview) => void
|
|
41
|
+
upsertConsensus: (s: ConsensusSession) => void
|
|
42
|
+
upsertClarity: (r: ClarityReview) => void
|
|
43
|
+
upsertBrainstorm: (s: BrainstormSession) => void
|
|
44
|
+
upsertKaizen: (g: KaizenGrading) => void
|
|
45
|
+
upsertInitiative: (i: Initiative) => void
|
|
46
|
+
upsertDocInterview: (s: DocInterviewSession) => void
|
|
47
|
+
/** Debounced full `workspace.refresh()`: the fallback for a change no payload can state. */
|
|
48
|
+
refreshBoard: () => void
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Route one pushed workspace event into the stores.
|
|
53
|
+
*
|
|
54
|
+
* The `board` branch is the one with a decision in it. A `board` event used to mean "re-fetch the
|
|
55
|
+
* whole snapshot", which on an active board is a REPLACE-style rehydrate of ~20 stores every ~300ms
|
|
56
|
+
* debounce window, for changes as small as one spawned task. The backend now carries the changed
|
|
57
|
+
* block whenever the change is fully described by it (see the `board` case in
|
|
58
|
+
* `@cat-factory/contracts`' `WorkspaceEvent`), so those patch in place exactly like an `execution`
|
|
59
|
+
* event's block does, through the same `upsert` whose monotonic stamp keeps a later stale refresh
|
|
60
|
+
* from clobbering them.
|
|
61
|
+
*
|
|
62
|
+
* A `board` event with NO block keeps the old behaviour, and that is not a fallback to tidy away:
|
|
63
|
+
* a removal, a reparent, a blueprint reconcile and every service-frame change genuinely need the
|
|
64
|
+
* refresh, because their new shape is not one block's contents.
|
|
65
|
+
*/
|
|
66
|
+
export function applyWorkspaceEvent(event: WorkspaceEvent, to: WorkspaceEventTargets): void {
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case 'execution':
|
|
69
|
+
// Full instance drives the step-level UI; agentRuns derives its coarse
|
|
70
|
+
// failure/retry summary from the same store, so no extra call is needed.
|
|
71
|
+
to.upsertExecution(event.instance)
|
|
72
|
+
if (event.block) to.upsertBlock(event.block)
|
|
73
|
+
return
|
|
74
|
+
case 'board':
|
|
75
|
+
// Targeted when the change fits in one block, coarse otherwise. Both shapes reach every
|
|
76
|
+
// board that mounts the affected service; only the cost differs.
|
|
77
|
+
if (event.block) to.upsertBlock(event.block)
|
|
78
|
+
else to.refreshBoard()
|
|
79
|
+
return
|
|
80
|
+
case 'bootstrap':
|
|
81
|
+
// Patch the run's live status/subtasks and its provisional/linked frame so
|
|
82
|
+
// the "bootstrapping…" card updates in place (then flips to a ready service
|
|
83
|
+
// or a failed badge) without a full refresh.
|
|
84
|
+
to.upsertBootstrap(event.job)
|
|
85
|
+
if (event.block) to.upsertBlock(event.block)
|
|
86
|
+
return
|
|
87
|
+
case 'env-config-repair':
|
|
88
|
+
// A provider config-repair run advanced: patch its live status/subtasks/outcome so the
|
|
89
|
+
// infrastructure-providers window's "repairing…" indicator updates in place (then flips to
|
|
90
|
+
// ok / residual issues / a failure) without a refetch. No board block.
|
|
91
|
+
to.upsertEnvConfigRepair(event.job)
|
|
92
|
+
return
|
|
93
|
+
case 'envTest':
|
|
94
|
+
// An ephemeral-environment self-test advanced a stage: patch the run so the service
|
|
95
|
+
// inspector's "Test environment creation" control shows the live stage + final outcome in
|
|
96
|
+
// place without a refetch. No board block.
|
|
97
|
+
to.upsertEnvironmentTest(event.run)
|
|
98
|
+
return
|
|
99
|
+
case 'infraSetup':
|
|
100
|
+
// The reachability watcher found a configured infrastructure area dead (or answering again):
|
|
101
|
+
// patch that one area so the setup banner appears/clears immediately. A full refresh would
|
|
102
|
+
// pay the whole snapshot aggregate for a one-field delta, and the projection the snapshot
|
|
103
|
+
// recomputes already folds the same recorded state.
|
|
104
|
+
to.patchInfraSetup(event.area, event.status, event.detail)
|
|
105
|
+
return
|
|
106
|
+
case 'notification':
|
|
107
|
+
// A PR needs a merge decision, a pipeline finished, or CI gave up: patch the
|
|
108
|
+
// inbox + per-block badge in place (resolved ones drop out of the inbox).
|
|
109
|
+
to.upsertNotification(event.notification)
|
|
110
|
+
return
|
|
111
|
+
case 'llmCall':
|
|
112
|
+
// A container agent just made a model call: fold the compact summary into the observability
|
|
113
|
+
// store so an open "Model activity" panel updates live (and keeps updating even when the
|
|
114
|
+
// durable driver is evicted, since the proxy emits these independently of the poll loop).
|
|
115
|
+
to.appendLlmCall(event.call)
|
|
116
|
+
return
|
|
117
|
+
case 'requirements':
|
|
118
|
+
// The async incorporate + re-review cycle changed a review's status: patch the cache so an
|
|
119
|
+
// open review window / inspector reflects it live ("incorporating…" → the next cycle /
|
|
120
|
+
// converged). The summons back, when needed, arrives as a `notification`.
|
|
121
|
+
to.upsertRequirements(event.review)
|
|
122
|
+
return
|
|
123
|
+
case 'consensus':
|
|
124
|
+
// A consensus session advanced (a round landed, the synthesis completed, or it failed):
|
|
125
|
+
// patch the cache so an open Consensus Session window renders the multi-model process live,
|
|
126
|
+
// round by round.
|
|
127
|
+
to.upsertConsensus(event.session)
|
|
128
|
+
return
|
|
129
|
+
case 'clarity':
|
|
130
|
+
// The clarity mirror of `requirements`.
|
|
131
|
+
to.upsertClarity(event.review)
|
|
132
|
+
return
|
|
133
|
+
case 'brainstorm':
|
|
134
|
+
// The async incorporate + re-run cycle changed a brainstorm session's status: patch the
|
|
135
|
+
// cache so an open brainstorm window / inspector reflects it live.
|
|
136
|
+
to.upsertBrainstorm(event.session)
|
|
137
|
+
return
|
|
138
|
+
case 'kaizen':
|
|
139
|
+
// A post-run Kaizen grading was scheduled, started or completed: fold it into the run cache
|
|
140
|
+
// (so an open run window shows scheduled→running→complete live) and the Kaizen screen
|
|
141
|
+
// history. Never surfaced on the board.
|
|
142
|
+
to.upsertKaizen(event.grading)
|
|
143
|
+
return
|
|
144
|
+
case 'initiative':
|
|
145
|
+
// An initiative changed (created, plan ingested, an item settled): patch the cache so an
|
|
146
|
+
// open tracker window / the board card reflects the transition live.
|
|
147
|
+
to.upsertInitiative(event.initiative)
|
|
148
|
+
return
|
|
149
|
+
case 'docInterview':
|
|
150
|
+
// The interactive document interview advanced (a fresh batch of questions, an answer, or
|
|
151
|
+
// convergence): patch the cache so an open interview window reflects it live.
|
|
152
|
+
to.upsertDocInterview(event.session)
|
|
153
|
+
return
|
|
154
|
+
default:
|
|
155
|
+
return dropUnknownEvent(event)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The exhaustiveness guard for the routing above.
|
|
161
|
+
*
|
|
162
|
+
* The COMPILE-TIME half is the point: `never` fails the build the moment `WorkspaceEvent` gains a
|
|
163
|
+
* member with no case, so a new pushed event cannot ship as a branch that silently does nothing.
|
|
164
|
+
* The spec's per-type table cannot do that job: a member absent from a hand-written list is just
|
|
165
|
+
* absent, and the suite stays green.
|
|
166
|
+
*
|
|
167
|
+
* At RUNTIME this deliberately drops the event. A backend one release ahead of the SPA legitimately
|
|
168
|
+
* pushes types this build has never heard of, and the connection carries every other event for the
|
|
169
|
+
* whole workspace: throwing would trade one unknown payload for the entire live session.
|
|
170
|
+
*/
|
|
171
|
+
function dropUnknownEvent(event: never): void {
|
|
172
|
+
void event
|
|
173
|
+
}
|
package/app/stores/agents.ts
CHANGED
|
@@ -69,9 +69,11 @@ export const useAgentsStore = defineStore('agents', () => {
|
|
|
69
69
|
* mapped to display metadata, de-duplicated, and never shadowing a built-in or
|
|
70
70
|
* system kind. The old `registerCustomKinds` only guarded `AGENT_BY_KIND`; this
|
|
71
71
|
* intentionally ALSO drops any custom kind colliding with a `SYSTEM_AGENT_META`
|
|
72
|
-
* kind (`ci` / `merger` /
|
|
73
|
-
*
|
|
74
|
-
*
|
|
72
|
+
* kind (`ci` / `merger` / gates …), so a snapshot can't override an engine kind's
|
|
73
|
+
* palette entry either — matching `agentKindMeta`'s precedence (built-in → system
|
|
74
|
+
* → custom), where a colliding custom kind would never win anyway. Note the cost of
|
|
75
|
+
* that guard: a SYSTEM_AGENT_META entry silently removes a registered kind from the
|
|
76
|
+
* palette, so the map must stay limited to kinds the engine inserts itself.
|
|
75
77
|
*/
|
|
76
78
|
const customArchetypes = computed<AgentArchetype[]>(() => {
|
|
77
79
|
const seen = new Set<string>()
|
package/app/stores/documents.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
DocumentOrigin,
|
|
10
10
|
DocumentSourceDescriptor,
|
|
11
11
|
DocumentSourceKind,
|
|
12
|
+
ResolvedDocumentRef,
|
|
12
13
|
SourceDocument,
|
|
13
14
|
} from '~/types/domain'
|
|
14
15
|
import { isConnectableSource } from '@cat-factory/contracts'
|
|
@@ -82,6 +83,16 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
82
83
|
documents.value = await api.listDocuments(workspace.requireId())
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Canonicalise a pasted URL/id into the reference this source would store it under, WITHOUT
|
|
88
|
+
* importing it. The backend's providers own the rule, so the picker validates against the same
|
|
89
|
+
* parse the import will run rather than a second copy of it that can drift; a ref the source
|
|
90
|
+
* cannot read comes back as a 422 whose `details.reason` says which correction it needs.
|
|
91
|
+
*/
|
|
92
|
+
function resolveRef(source: DocumentSourceKind, ref: string): Promise<ResolvedDocumentRef> {
|
|
93
|
+
return api.resolveDocumentRef(workspace.requireId(), source, { ref })
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
/** Import (fetch + persist) a page by id or URL from a source. */
|
|
86
97
|
async function importDocument(source: DocumentSourceKind, ref: string): Promise<SourceDocument> {
|
|
87
98
|
loading.value = true
|
|
@@ -211,6 +222,7 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
211
222
|
connect,
|
|
212
223
|
disconnect,
|
|
213
224
|
loadDocuments,
|
|
225
|
+
resolveRef,
|
|
214
226
|
importDocument,
|
|
215
227
|
search,
|
|
216
228
|
plan,
|
package/app/types/documents.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
AGENT_ARCHETYPES,
|
|
5
5
|
AGENT_BY_KIND,
|
|
6
6
|
BLOCK_TYPE_META,
|
|
7
|
+
COMPANION_FOR_PRODUCER,
|
|
8
|
+
MODEL_CONFIGURABLE_SYSTEM_KINDS,
|
|
7
9
|
STATUS_META,
|
|
8
10
|
SYSTEM_AGENT_META,
|
|
9
11
|
agentKindMeta,
|
|
@@ -20,9 +22,12 @@ const AGENT_KINDS: AgentKind[] = [
|
|
|
20
22
|
'pr-reviewer',
|
|
21
23
|
'spike',
|
|
22
24
|
'task-estimator',
|
|
25
|
+
'spec-writer',
|
|
26
|
+
'blueprints',
|
|
23
27
|
'architect',
|
|
24
28
|
'researcher',
|
|
25
29
|
'coder',
|
|
30
|
+
'deployer',
|
|
26
31
|
'tester-api',
|
|
27
32
|
'tester-ui',
|
|
28
33
|
'reviewer',
|
|
@@ -80,6 +85,38 @@ describe('catalog', () => {
|
|
|
80
85
|
expect(basic).toEqual(expect.arrayContaining(['architect', 'coder', 'tester-api']))
|
|
81
86
|
})
|
|
82
87
|
|
|
88
|
+
it('never shadows a companion producer as a system kind', () => {
|
|
89
|
+
// A companion is never placed directly: the builder renders it as a toggle on its producer
|
|
90
|
+
// step, so a producer that cannot be placed takes its companion out of the builder with it.
|
|
91
|
+
// A producer reaches the palette either statically (AGENT_ARCHETYPES) or from the backend
|
|
92
|
+
// registry — and an entry in SYSTEM_AGENT_META DROPS the registry's copy (see the agents
|
|
93
|
+
// store's `customArchetypes`), which is how `spec-writer` and its `spec-companion` both
|
|
94
|
+
// became unreachable. The shadow is the half this file owns, so it is the half asserted.
|
|
95
|
+
for (const producer of Object.keys(COMPANION_FOR_PRODUCER)) {
|
|
96
|
+
expect(
|
|
97
|
+
producer in SYSTEM_AGENT_META,
|
|
98
|
+
`${producer} has a companion but is shadowed as a system kind, so neither can be placed`,
|
|
99
|
+
).toBe(false)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('resolves every kind the Model Defaults panel lists beside the palette', () => {
|
|
104
|
+
// The list is spelled as kind strings indexed into SYSTEM_AGENT_META through a non-null
|
|
105
|
+
// assertion, so a kind that MOVES to the palette (or is renamed) leaves an `undefined` in
|
|
106
|
+
// the array rather than a type error, and the panel renders a blank row it cannot pin a
|
|
107
|
+
// model on. Assert the relation the assertion claims.
|
|
108
|
+
for (const entry of MODEL_CONFIGURABLE_SYSTEM_KINDS) {
|
|
109
|
+
expect(entry, 'MODEL_CONFIGURABLE_SYSTEM_KINDS names a kind with no metadata').toBeDefined()
|
|
110
|
+
expect(entry.kind).toEqual(expect.any(String))
|
|
111
|
+
}
|
|
112
|
+
// And nothing is offered twice: a palette archetype is already listed by the panel, so a
|
|
113
|
+
// kind appearing in both would render a duplicate row.
|
|
114
|
+
const palette = new Set(AGENT_ARCHETYPES.map((a) => a.kind))
|
|
115
|
+
for (const entry of MODEL_CONFIGURABLE_SYSTEM_KINDS) {
|
|
116
|
+
expect(palette.has(entry.kind), `${entry.kind} is listed twice in Model Defaults`).toBe(false)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
83
120
|
it('resolves usable metadata for every kind via agentKindMeta', () => {
|
|
84
121
|
// Palette archetypes resolve to their own entry.
|
|
85
122
|
for (const a of AGENT_ARCHETYPES) {
|
|
@@ -88,8 +125,6 @@ describe('catalog', () => {
|
|
|
88
125
|
// Engine system kinds (present in seeded pipelines but not the palette) resolve
|
|
89
126
|
// to their system metadata rather than blowing up an undefined access.
|
|
90
127
|
for (const kind of [
|
|
91
|
-
'spec-writer',
|
|
92
|
-
'blueprints',
|
|
93
128
|
'conflicts',
|
|
94
129
|
'conflict-resolver',
|
|
95
130
|
'ci',
|
package/app/utils/catalog.ts
CHANGED
|
@@ -136,6 +136,21 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
136
136
|
'A structured dialogue that explores and finalizes a technical approach from the refined requirements — proposing options with explicit trade-offs and letting you converge, before the architect.',
|
|
137
137
|
resultView: 'brainstorm',
|
|
138
138
|
},
|
|
139
|
+
{
|
|
140
|
+
// Authors the service's in-repo specification from the clarified requirements, so it sits
|
|
141
|
+
// beside the design kinds and ahead of the architect that reads what it wrote. Registered on
|
|
142
|
+
// the backend so it also arrives via the workspace manifest, and modelled statically here for
|
|
143
|
+
// the same reason `pr-reviewer` is: a `pl_bugfix` / `pl_spec` timeline must name the step
|
|
144
|
+
// before the manifest hydrates. Mirrors the backend `presentation` in `spec-blueprints.ts`.
|
|
145
|
+
kind: 'spec-writer',
|
|
146
|
+
tier: 'intermediate',
|
|
147
|
+
label: 'Spec Writer',
|
|
148
|
+
icon: 'i-lucide-clipboard-list',
|
|
149
|
+
color: '#c084fc',
|
|
150
|
+
category: 'design',
|
|
151
|
+
description:
|
|
152
|
+
"Aggregates every task's clarified requirements into the service's in-repo specification (spec.json) with full acceptance-scenario coverage, derived into Gherkin.",
|
|
153
|
+
},
|
|
139
154
|
{
|
|
140
155
|
kind: 'architect',
|
|
141
156
|
tier: 'basic',
|
|
@@ -145,6 +160,18 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
145
160
|
category: 'design',
|
|
146
161
|
description: 'Designs the shape of the solution and breaks down the work.',
|
|
147
162
|
},
|
|
163
|
+
{
|
|
164
|
+
// Refreshes the service → modules map the board projects. Statically modelled beside its
|
|
165
|
+
// backend `presentation` for the same reason the Spec Writer is: `pl_blueprint` timelines
|
|
166
|
+
// render before the manifest hydrates.
|
|
167
|
+
kind: 'blueprints',
|
|
168
|
+
tier: 'intermediate',
|
|
169
|
+
label: 'Blueprinter',
|
|
170
|
+
icon: 'i-lucide-map',
|
|
171
|
+
color: '#22d3ee',
|
|
172
|
+
category: 'design',
|
|
173
|
+
description: 'Maps the repository into the service → modules blueprint.',
|
|
174
|
+
},
|
|
148
175
|
{
|
|
149
176
|
kind: 'researcher',
|
|
150
177
|
tier: 'intermediate',
|
|
@@ -181,6 +208,21 @@ export const AGENT_ARCHETYPES: AgentArchetype[] = [
|
|
|
181
208
|
category: 'build',
|
|
182
209
|
description: 'Builds WireMock mocks for external services and wires them into local/CI runs.',
|
|
183
210
|
},
|
|
211
|
+
{
|
|
212
|
+
// Provisions the ephemeral environment the tester / human-test / playwright steps read, which
|
|
213
|
+
// is why it leads the testing group. A palette block for the same reason `disposer` is one:
|
|
214
|
+
// `assertDeployerBeforeConsumer` REFUSES a run whose chain reaches an env consumer with no
|
|
215
|
+
// Deployer in front of it on a deployable service, and a hand-built pipeline that hits that
|
|
216
|
+
// refusal has no reseed to fall back on.
|
|
217
|
+
kind: 'deployer',
|
|
218
|
+
tier: 'intermediate',
|
|
219
|
+
label: 'Deployer',
|
|
220
|
+
icon: 'i-lucide-cloud-upload',
|
|
221
|
+
color: '#34d399',
|
|
222
|
+
category: 'test',
|
|
223
|
+
description:
|
|
224
|
+
'Provisions the ephemeral environment the tester and human-test gate run against (kubernetes / custom services); a no-op for docker-compose / infraless. Place it before the first step that needs the environment.',
|
|
225
|
+
},
|
|
184
226
|
{
|
|
185
227
|
kind: 'tester-api',
|
|
186
228
|
tier: 'basic',
|
|
@@ -491,29 +533,22 @@ export function isTesterKind(kind: string): boolean {
|
|
|
491
533
|
|
|
492
534
|
/**
|
|
493
535
|
* Display metadata for the engine-driven "system" kinds — the gate/automation
|
|
494
|
-
* steps (
|
|
495
|
-
*
|
|
496
|
-
*
|
|
536
|
+
* steps (conflicts gate + resolver, CI gate + fixer, merger) that appear in
|
|
537
|
+
* seeded pipelines and run timelines but are NOT user-addable palette
|
|
538
|
+
* archetypes, so they're intentionally absent from {@link AGENT_ARCHETYPES}
|
|
497
539
|
* / {@link AGENT_BY_KIND}. Looked up through {@link agentKindMeta}.
|
|
540
|
+
*
|
|
541
|
+
* An entry here also SHADOWS the backend's own catalog: the agents store drops any
|
|
542
|
+
* registered kind whose id appears in this map (see `customArchetypes`), so listing a
|
|
543
|
+
* kind that declares `presentation` silently overrides the deployment's decision to
|
|
544
|
+
* offer it. That is how `spec-writer` and `blueprints` stayed out of the palette while
|
|
545
|
+
* both collapse docs promised them as opt-in builder steps, and it took
|
|
546
|
+
* `spec-companion` with them: a companion renders as a toggle on its producer, so a
|
|
547
|
+
* shadowed producer removes both. Add a kind here only when the ENGINE decides the
|
|
548
|
+
* step exists — a gate it inserts, a helper it escalates to — and never when the
|
|
549
|
+
* backend registers it as a palette block.
|
|
498
550
|
*/
|
|
499
551
|
export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
|
|
500
|
-
'spec-writer': {
|
|
501
|
-
kind: 'spec-writer',
|
|
502
|
-
tier: 'intermediate',
|
|
503
|
-
label: 'Spec Writer',
|
|
504
|
-
icon: 'i-lucide-clipboard-list',
|
|
505
|
-
color: '#c084fc',
|
|
506
|
-
description:
|
|
507
|
-
"Aggregates every task's clarified requirements into the service's in-repo specification (spec.json) with full acceptance-scenario coverage, derived into Gherkin.",
|
|
508
|
-
},
|
|
509
|
-
blueprints: {
|
|
510
|
-
kind: 'blueprints',
|
|
511
|
-
tier: 'intermediate',
|
|
512
|
-
label: 'Blueprinter',
|
|
513
|
-
icon: 'i-lucide-map',
|
|
514
|
-
color: '#22d3ee',
|
|
515
|
-
description: 'Maps the repository into the service → modules blueprint.',
|
|
516
|
-
},
|
|
517
552
|
// The read-only Challenge Investigator: dispatched off a parked `pr-reviewer` step when a human
|
|
518
553
|
// challenges a finding, it re-examines that ONE finding against the full source and upholds
|
|
519
554
|
// (strengthening) or retracts it. Never a palette block; modelled here purely so it is a
|
|
@@ -529,19 +564,6 @@ export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
|
|
|
529
564
|
'Re-examines a single challenged PR-review finding against the full source, then upholds ' +
|
|
530
565
|
'(strengthening it) or retracts it with a justification. Configurable separately from the reviewer.',
|
|
531
566
|
},
|
|
532
|
-
// The single environment provisioner: an operational (non-LLM) step that stands up the ephemeral
|
|
533
|
-
// environment the tester / human-test gate run against for a kubernetes/custom service, and is a
|
|
534
|
-
// fast no-op for docker-compose / infraless. Seeded before the first tester/human-test step in the
|
|
535
|
-
// built-in pipelines, so it needs display metadata (else it renders as a generic gray "Agent").
|
|
536
|
-
deployer: {
|
|
537
|
-
kind: 'deployer',
|
|
538
|
-
tier: 'intermediate',
|
|
539
|
-
label: 'Deployer',
|
|
540
|
-
icon: 'i-lucide-cloud-upload',
|
|
541
|
-
color: '#34d399',
|
|
542
|
-
description:
|
|
543
|
-
'Provisions the ephemeral environment the tester and human-test gate run against (kubernetes / custom services); a no-op for docker-compose / infraless.',
|
|
544
|
-
},
|
|
545
567
|
// The Initiative Planning pipeline's steps. Only runnable on an initiative
|
|
546
568
|
// block (pl_initiative — enforced by the engine), so they are display-metadata
|
|
547
569
|
// system kinds, never palette archetypes. The analyst runs FIRST, ahead of the
|
|
@@ -766,8 +788,6 @@ export const OBSERVABILITY_GATE_ARCHETYPE: AgentArchetype =
|
|
|
766
788
|
*/
|
|
767
789
|
export const MODEL_CONFIGURABLE_SYSTEM_KINDS: AgentArchetype[] = [
|
|
768
790
|
...[
|
|
769
|
-
'spec-writer',
|
|
770
|
-
'blueprints',
|
|
771
791
|
'initiative-planner',
|
|
772
792
|
'conflict-resolver',
|
|
773
793
|
'ci-fixer',
|
|
@@ -798,7 +818,7 @@ const FALLBACK_AGENT_META: Omit<AgentArchetype, 'kind'> = {
|
|
|
798
818
|
* {@link customAgentKindMeta} by the agents store), or an unknown one — ALWAYS
|
|
799
819
|
* returning a usable icon/label/color. This is the single lookup every pipeline
|
|
800
820
|
* / run renderer should use so a kind missing from the archetype map (e.g.
|
|
801
|
-
* `ci`/`merger
|
|
821
|
+
* `ci`/`merger` in a seeded pipeline) can never blow up a component
|
|
802
822
|
* with an undefined access. Reading `customAgentKindMeta` reactively means a
|
|
803
823
|
* component computed re-runs when the custom catalog changes.
|
|
804
824
|
*/
|
package/i18n/locales/de.json
CHANGED
|
@@ -2818,7 +2818,8 @@
|
|
|
2818
2818
|
"derivedTitleFallback": "Pull Request prüfen",
|
|
2819
2819
|
"prNotFound": "Pull Request #{number} wurde im Repository dieses Service nicht gefunden. Prüfe die Nummer, oder verknüpfe den Service mit dem Repository, in dem der Pull Request liegt.",
|
|
2820
2820
|
"prRepoMismatch": "Dieser Pull Request liegt in einem anderen Repository. Dieser Service prüft {repo}; lege die Prüfaufgabe unter dem Service an, der mit dem Repository des Pull Requests verknüpft ist."
|
|
2821
|
-
}
|
|
2821
|
+
},
|
|
2822
|
+
"contextFailed": "Aufgabe nicht erstellt: {count} Anhang konnte nicht gelesen werden | Aufgabe nicht erstellt: {count} Anhänge konnten nicht gelesen werden"
|
|
2822
2823
|
},
|
|
2823
2824
|
"recurring": {
|
|
2824
2825
|
"title": "Eine wiederkehrende Pipeline hinzufügen",
|
|
@@ -3004,7 +3005,8 @@
|
|
|
3004
3005
|
"attachDocDisabledEnable": "Aktivieren Sie zuerst die Dokumente-Integration",
|
|
3005
3006
|
"attachIssueDisabledConnect": "Verbinden Sie zuerst einen Issue-Tracker (Integrationen)",
|
|
3006
3007
|
"attachIssueDisabledEnable": "Aktivieren Sie zuerst die Issue-Tracker-Integration",
|
|
3007
|
-
"importsOnAdd": "importiert beim Hinzufügen"
|
|
3008
|
+
"importsOnAdd": "importiert beim Hinzufügen",
|
|
3009
|
+
"unreadable": "Konnte nicht abgerufen werden: {error}"
|
|
3008
3010
|
},
|
|
3009
3011
|
"providers": {
|
|
3010
3012
|
"presetMismatch": {
|
|
@@ -3783,7 +3785,15 @@
|
|
|
3783
3785
|
"attachByReference": "{ref} per Referenz anhängen",
|
|
3784
3786
|
"noMatches": "Keine passenden Seiten.",
|
|
3785
3787
|
"emptySearchable": "Nach Titel suchen oder ein importiertes Dokument auswählen.",
|
|
3786
|
-
"emptyRefOnly": "Fügen Sie eine Seiten-URL oder -ID ein, um sie anzuhängen."
|
|
3788
|
+
"emptyRefOnly": "Fügen Sie eine Seiten-URL oder -ID ein, um sie anzuhängen.",
|
|
3789
|
+
"refChecking": "Referenz wird geprüft…",
|
|
3790
|
+
"refUnrecognized": "Keine {source}-Referenz. Erwartet: {expected}",
|
|
3791
|
+
"refOtherSource": "Das ist ein {claimed}-Link, kein {source}-Link.",
|
|
3792
|
+
"refSwitchSource": "Stattdessen {source} verwenden",
|
|
3793
|
+
"refTrimmed": "Auf die unterstützte Form gekürzt",
|
|
3794
|
+
"refWidened": "Nennt einen Frame, den diese Quelle nicht lesen kann ({scope}); daher wird die gesamte Datei angehängt.",
|
|
3795
|
+
"refAlreadyAttached": "Diese Referenz ist bereits angehängt.",
|
|
3796
|
+
"refCheckFailed": "Referenz konnte nicht geprüft werden: {error}"
|
|
3787
3797
|
},
|
|
3788
3798
|
"repoPicker": {
|
|
3789
3799
|
"searchRepoPlaceholder": "Repositorys suchen…",
|
|
@@ -5040,7 +5050,8 @@
|
|
|
5040
5050
|
"failedTitle": "Die Initiative konnte nicht erstellt werden",
|
|
5041
5051
|
"contextDocsHint": "Hänge eine Anforderung, ein RFC oder ein PRD an, damit die Planungsagenten es beim Abstecken und Entwerfen des Plans lesen.",
|
|
5042
5052
|
"contextIssuesHint": "Hänge ein Tracker-Issue an, damit die Planungsagenten beim Entwerfen des Plans seine Beschreibung und Kommentare sehen.",
|
|
5043
|
-
"linkFailed": "Initiative erstellt, aber {count} Anhang konnte nicht verknüpft werden | Initiative erstellt, aber {count} Anhänge konnten nicht verknüpft werden"
|
|
5053
|
+
"linkFailed": "Initiative erstellt, aber {count} Anhang konnte nicht verknüpft werden | Initiative erstellt, aber {count} Anhänge konnten nicht verknüpft werden",
|
|
5054
|
+
"contextFailed": "Initiative nicht erstellt: {count} Anhang konnte nicht gelesen werden | Initiative nicht erstellt: {count} Anhänge konnten nicht gelesen werden"
|
|
5044
5055
|
},
|
|
5045
5056
|
"status": {
|
|
5046
5057
|
"planning": "Planung",
|
package/i18n/locales/en.json
CHANGED
|
@@ -347,6 +347,10 @@
|
|
|
347
347
|
"derivedTitleFallback": "Review pull request",
|
|
348
348
|
"prNotFound": "Pull request #{number} was not found in this service's repository. Check the number, or link the service to the repository that pull request is on.",
|
|
349
349
|
"prRepoMismatch": "That pull request is on a different repository. This service reviews {repo}, so create the review task under the service linked to the pull request's repository."
|
|
350
|
+
},
|
|
351
|
+
"contextFailed": "Task not created: {count} attachment could not be read | Task not created: {count} attachments could not be read",
|
|
352
|
+
"@contextFailed": {
|
|
353
|
+
"description": "Count-based: how many context attachments (docs/issues) could not be fetched, which is why nothing was created (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
350
354
|
}
|
|
351
355
|
},
|
|
352
356
|
"recurring": {
|
|
@@ -548,7 +552,8 @@
|
|
|
548
552
|
"attachDocDisabledEnable": "Enable the documents integration first",
|
|
549
553
|
"attachIssueDisabledConnect": "Connect an issue tracker first (Integrations)",
|
|
550
554
|
"attachIssueDisabledEnable": "Enable the issue-tracker integration first",
|
|
551
|
-
"importsOnAdd": "imports on add"
|
|
555
|
+
"importsOnAdd": "imports on add",
|
|
556
|
+
"unreadable": "Could not be fetched: {error}"
|
|
552
557
|
},
|
|
553
558
|
"errors": {
|
|
554
559
|
"generic": {
|
|
@@ -4300,7 +4305,15 @@
|
|
|
4300
4305
|
"attachByReference": "Attach {ref} by reference",
|
|
4301
4306
|
"noMatches": "No matching pages.",
|
|
4302
4307
|
"emptySearchable": "Search by title, or pick an imported document.",
|
|
4303
|
-
"emptyRefOnly": "Paste a page URL or ID to attach it."
|
|
4308
|
+
"emptyRefOnly": "Paste a page URL or ID to attach it.",
|
|
4309
|
+
"refChecking": "Checking the reference…",
|
|
4310
|
+
"refUnrecognized": "Not a {source} reference. Expected {expected}",
|
|
4311
|
+
"refOtherSource": "That is a {claimed} link, not a {source} one.",
|
|
4312
|
+
"refSwitchSource": "Use {source} instead",
|
|
4313
|
+
"refTrimmed": "Trimmed to the supported form",
|
|
4314
|
+
"refWidened": "Names a frame this source cannot read ({scope}), so the whole file is attached.",
|
|
4315
|
+
"refAlreadyAttached": "This reference is already attached.",
|
|
4316
|
+
"refCheckFailed": "Could not check the reference: {error}"
|
|
4304
4317
|
},
|
|
4305
4318
|
"repoPicker": {
|
|
4306
4319
|
"searchRepoPlaceholder": "Search repositories…",
|
|
@@ -6515,6 +6528,10 @@
|
|
|
6515
6528
|
"linkFailed": "Initiative created, but {count} attachment could not be linked | Initiative created, but {count} attachments could not be linked",
|
|
6516
6529
|
"@linkFailed": {
|
|
6517
6530
|
"description": "Count-based: how many context attachments (docs/issues) failed to link after the initiative was created (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
6531
|
+
},
|
|
6532
|
+
"contextFailed": "Initiative not created: {count} attachment could not be read | Initiative not created: {count} attachments could not be read",
|
|
6533
|
+
"@contextFailed": {
|
|
6534
|
+
"description": "Count-based: how many context attachments (docs/issues) could not be fetched, which is why nothing was created (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
|
|
6518
6535
|
}
|
|
6519
6536
|
},
|
|
6520
6537
|
"status": {
|
package/i18n/locales/es.json
CHANGED
|
@@ -311,7 +311,8 @@
|
|
|
311
311
|
"derivedTitleFallback": "Revisar la pull request",
|
|
312
312
|
"prNotFound": "No se encontró la pull request n.º {number} en el repositorio de este servicio. Comprueba el número, o vincula el servicio al repositorio en el que está esa pull request.",
|
|
313
313
|
"prRepoMismatch": "Esa pull request está en otro repositorio. Este servicio revisa {repo}, así que crea la tarea de revisión en el servicio vinculado al repositorio de la pull request."
|
|
314
|
-
}
|
|
314
|
+
},
|
|
315
|
+
"contextFailed": "Tarea no creada: no se pudo leer {count} adjunto | Tarea no creada: no se pudieron leer {count} adjuntos"
|
|
315
316
|
},
|
|
316
317
|
"recurring": {
|
|
317
318
|
"title": "Añadir una pipeline recurrente",
|
|
@@ -497,7 +498,8 @@
|
|
|
497
498
|
"attachDocDisabledEnable": "Activa primero la integración de documentos",
|
|
498
499
|
"attachIssueDisabledConnect": "Conecta primero un gestor de incidencias (Integraciones)",
|
|
499
500
|
"attachIssueDisabledEnable": "Activa primero la integración del gestor de incidencias",
|
|
500
|
-
"importsOnAdd": "se importa al añadir"
|
|
501
|
+
"importsOnAdd": "se importa al añadir",
|
|
502
|
+
"unreadable": "No se pudo obtener: {error}"
|
|
501
503
|
},
|
|
502
504
|
"errors": {
|
|
503
505
|
"generic": {
|
|
@@ -4166,7 +4168,15 @@
|
|
|
4166
4168
|
"attachByReference": "Adjuntar {ref} por referencia",
|
|
4167
4169
|
"noMatches": "No hay páginas que coincidan.",
|
|
4168
4170
|
"emptySearchable": "Busca por título o elige un documento importado.",
|
|
4169
|
-
"emptyRefOnly": "Pega la URL o el ID de una página para adjuntarla."
|
|
4171
|
+
"emptyRefOnly": "Pega la URL o el ID de una página para adjuntarla.",
|
|
4172
|
+
"refChecking": "Comprobando la referencia…",
|
|
4173
|
+
"refUnrecognized": "No es una referencia de {source}. Se esperaba {expected}",
|
|
4174
|
+
"refOtherSource": "Es un enlace de {claimed}, no de {source}.",
|
|
4175
|
+
"refSwitchSource": "Usar {source} en su lugar",
|
|
4176
|
+
"refTrimmed": "Recortado al formato admitido",
|
|
4177
|
+
"refWidened": "Nombra un marco que esta fuente no puede leer ({scope}), así que se adjunta el archivo completo.",
|
|
4178
|
+
"refAlreadyAttached": "Esta referencia ya está adjunta.",
|
|
4179
|
+
"refCheckFailed": "No se pudo comprobar la referencia: {error}"
|
|
4170
4180
|
},
|
|
4171
4181
|
"repoPicker": {
|
|
4172
4182
|
"searchRepoPlaceholder": "Buscar repositorios…",
|
|
@@ -6296,7 +6306,8 @@
|
|
|
6296
6306
|
"failedTitle": "No se pudo crear la iniciativa",
|
|
6297
6307
|
"contextDocsHint": "Adjunta un requisito, RFC o PRD para que los agentes de planificación lo lean al delimitar y redactar el plan.",
|
|
6298
6308
|
"contextIssuesHint": "Adjunta una incidencia para que los agentes de planificación vean su descripción y comentarios al redactar el plan.",
|
|
6299
|
-
"linkFailed": "Iniciativa creada, pero no se pudo vincular {count} adjunto | Iniciativa creada, pero no se pudieron vincular {count} adjuntos"
|
|
6309
|
+
"linkFailed": "Iniciativa creada, pero no se pudo vincular {count} adjunto | Iniciativa creada, pero no se pudieron vincular {count} adjuntos",
|
|
6310
|
+
"contextFailed": "Iniciativa no creada: no se pudo leer {count} adjunto | Iniciativa no creada: no se pudieron leer {count} adjuntos"
|
|
6300
6311
|
},
|
|
6301
6312
|
"status": {
|
|
6302
6313
|
"planning": "Planificando",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -311,7 +311,8 @@
|
|
|
311
311
|
"derivedTitleFallback": "Examiner la pull request",
|
|
312
312
|
"prNotFound": "La pull request n° {number} est introuvable dans le dépôt de ce service. Vérifiez le numéro, ou reliez le service au dépôt qui héberge cette pull request.",
|
|
313
313
|
"prRepoMismatch": "Cette pull request se trouve dans un autre dépôt. Ce service examine {repo} : créez la tâche de revue sous le service relié au dépôt de la pull request."
|
|
314
|
-
}
|
|
314
|
+
},
|
|
315
|
+
"contextFailed": "Tâche non créée : {count} pièce jointe illisible | Tâche non créée : {count} pièces jointes illisibles"
|
|
315
316
|
},
|
|
316
317
|
"recurring": {
|
|
317
318
|
"title": "Ajouter une pipeline récurrente",
|
|
@@ -497,7 +498,8 @@
|
|
|
497
498
|
"attachDocDisabledEnable": "Activez d’abord l’intégration de documents",
|
|
498
499
|
"attachIssueDisabledConnect": "Connectez d’abord un suivi de tickets (Intégrations)",
|
|
499
500
|
"attachIssueDisabledEnable": "Activez d’abord l’intégration du suivi de tickets",
|
|
500
|
-
"importsOnAdd": "importé à l’ajout"
|
|
501
|
+
"importsOnAdd": "importé à l’ajout",
|
|
502
|
+
"unreadable": "Récupération impossible : {error}"
|
|
501
503
|
},
|
|
502
504
|
"errors": {
|
|
503
505
|
"generic": {
|
|
@@ -4166,7 +4168,15 @@
|
|
|
4166
4168
|
"attachByReference": "Joindre {ref} par référence",
|
|
4167
4169
|
"noMatches": "Aucune page correspondante.",
|
|
4168
4170
|
"emptySearchable": "Recherchez par titre ou choisissez un document importé.",
|
|
4169
|
-
"emptyRefOnly": "Collez l'URL ou l'ID d'une page pour la joindre."
|
|
4171
|
+
"emptyRefOnly": "Collez l'URL ou l'ID d'une page pour la joindre.",
|
|
4172
|
+
"refChecking": "Vérification de la référence…",
|
|
4173
|
+
"refUnrecognized": "Ce n'est pas une référence {source}. Format attendu : {expected}",
|
|
4174
|
+
"refOtherSource": "C'est un lien {claimed}, pas un lien {source}.",
|
|
4175
|
+
"refSwitchSource": "Utiliser {source} à la place",
|
|
4176
|
+
"refTrimmed": "Réduit au format pris en charge",
|
|
4177
|
+
"refWidened": "Désigne un cadre que cette source ne peut pas lire ({scope}) : le fichier entier est donc joint.",
|
|
4178
|
+
"refAlreadyAttached": "Cette référence est déjà jointe.",
|
|
4179
|
+
"refCheckFailed": "Impossible de vérifier la référence : {error}"
|
|
4170
4180
|
},
|
|
4171
4181
|
"repoPicker": {
|
|
4172
4182
|
"searchRepoPlaceholder": "Rechercher des dépôts…",
|
|
@@ -6296,7 +6306,8 @@
|
|
|
6296
6306
|
"failedTitle": "Impossible de creer l'initiative",
|
|
6297
6307
|
"contextDocsHint": "Joignez une exigence, une RFC ou un PRD pour que les agents de planification la lisent en cadrant et en rédigeant le plan.",
|
|
6298
6308
|
"contextIssuesHint": "Joignez un ticket pour que les agents de planification voient sa description et ses commentaires en rédigeant le plan.",
|
|
6299
|
-
"linkFailed": "Initiative créée, mais {count} pièce jointe n’a pas pu être liée | Initiative créée, mais {count} pièces jointes n’ont pas pu être liées"
|
|
6309
|
+
"linkFailed": "Initiative créée, mais {count} pièce jointe n’a pas pu être liée | Initiative créée, mais {count} pièces jointes n’ont pas pu être liées",
|
|
6310
|
+
"contextFailed": "Initiative non créée : {count} pièce jointe illisible | Initiative non créée : {count} pièces jointes illisibles"
|
|
6300
6311
|
},
|
|
6301
6312
|
"status": {
|
|
6302
6313
|
"planning": "Planification",
|