@cat-factory/app 0.182.0 → 0.182.1
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 +7 -3
- package/app/components/board/nodes/BlockNode.vue +14 -4
- package/app/components/board/nodes/InitiativeCard.vue +27 -2
- package/app/components/initiative/InitiativeTrackerWindow.vue +149 -9
- package/app/components/panels/inspector/InitiativeInspector.vue +23 -2
- package/app/composables/useInitiativePlanning.ts +70 -0
- package/app/modular/nav-contributions.spec.ts +13 -3
- package/app/modular/nav-contributions.ts +42 -32
- package/app/utils/initiative.spec.ts +51 -1
- package/app/utils/initiative.ts +44 -0
- package/i18n/locales/de.json +10 -0
- package/i18n/locales/en.json +10 -0
- package/i18n/locales/es.json +10 -0
- package/i18n/locales/fr.json +10 -0
- package/i18n/locales/he.json +10 -0
- package/i18n/locales/it.json +10 -0
- package/i18n/locales/ja.json +10 -0
- package/i18n/locales/pl.json +10 -0
- package/i18n/locales/tr.json +10 -0
- package/i18n/locales/uk.json +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,9 +177,13 @@ Two placements are load-bearing enough to state, because putting a new one in th
|
|
|
177
177
|
wrong place is invisible until a user cannot find it:
|
|
178
178
|
|
|
179
179
|
- **The sidebar section is a claim about what the destination IS.** `models` is the
|
|
180
|
-
engines,
|
|
181
|
-
|
|
182
|
-
`
|
|
180
|
+
model layer — the engines, the per-agent model choice, and the surfaces that
|
|
181
|
+
evaluate a prompt+agent+model combination (Sandbox, Kaizen); `integrations` the
|
|
182
|
+
optional EXTERNAL systems; `infrastructure` where agent containers and test
|
|
183
|
+
environments run; `configuration` workspace/account settings. A surface that
|
|
184
|
+
connects to nothing does not belong in `integrations` however configuration-shaped
|
|
185
|
+
it feels. `nav-contributions.spec.ts` pins the section order and each section's
|
|
186
|
+
membership.
|
|
183
187
|
- **A flow that edits ONE entity's config is a section of the window that owns that
|
|
184
188
|
config, not a sibling nav entry.** The guided Docker Compose environment setup
|
|
185
189
|
(`ComposeEnvironmentSetupSection.vue` → `EnvironmentSetupWizard.vue`) lives inside
|
|
@@ -83,12 +83,22 @@ const selected = computed(() => ui.selectedBlockId === props.id)
|
|
|
83
83
|
// kept (gated off) so the prior behaviour is one edit away if we want chips back.
|
|
84
84
|
const showExpanded = computed(() => true)
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// Every child whose parked run the frame badge speaks for: its tasks AND its initiative
|
|
87
|
+
// blocks. An initiative is a frame child like a module and runs an ordinary pipeline (its
|
|
88
|
+
// planner parks on a real approval gate), so leaving it out made a whole class of parked run
|
|
89
|
+
// invisible at frame level — the badge read "nothing needs you" while a plan sat waiting.
|
|
90
|
+
const attentionIds = computed(() => {
|
|
91
|
+
const ids = new Set(taskIds.value)
|
|
92
|
+
for (const i of initiativeBlocks.value) ids.add(i.id)
|
|
93
|
+
return ids
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Surface a pending decision from this frame OR any of its children (O(children) map
|
|
87
97
|
// lookups, not a scan of every open decision per frame).
|
|
88
98
|
const blockDecisions = computed(() => {
|
|
89
99
|
const byBlock = execution.decisionsByBlock
|
|
90
100
|
const out = [...(byBlock.get(props.id) ?? [])]
|
|
91
|
-
for (const id of
|
|
101
|
+
for (const id of attentionIds.value) {
|
|
92
102
|
const list = byBlock.get(id)
|
|
93
103
|
if (list) out.push(...list)
|
|
94
104
|
}
|
|
@@ -100,14 +110,14 @@ function openFirstDecision() {
|
|
|
100
110
|
if (d) ui.openDecision(d.instanceId, d.decision.id)
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
// Surface a pending approval gate from this frame OR any of its
|
|
113
|
+
// Surface a pending approval gate from this frame OR any of its children — but NOT an
|
|
104
114
|
// iterative reviewer gate (requirements-review / clarity-review) that's mid-cycle
|
|
105
115
|
// (incorporating / re-reviewing in the driver), which is background work needing no human,
|
|
106
116
|
// so it stays off the frame's "Approval" badge.
|
|
107
117
|
const blockApprovals = computed(() => {
|
|
108
118
|
const byBlock = execution.approvalsByBlock
|
|
109
119
|
const candidates = [...(byBlock.get(props.id) ?? [])]
|
|
110
|
-
for (const id of
|
|
120
|
+
for (const id of attentionIds.value) {
|
|
111
121
|
const list = byBlock.get(id)
|
|
112
122
|
if (list) candidates.push(...list)
|
|
113
123
|
}
|
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
// initiative's equivalent "Run planning" (and, while parked mid-interview, "Answer
|
|
6
6
|
// planning questions") lives right here on the board — the same actions the
|
|
7
7
|
// inspector offers — so starting an initiative isn't hidden behind selecting it.
|
|
8
|
+
// It likewise mirrors a task card's `attention` affordance: a planning run parked
|
|
9
|
+
// on the plan-approval gate (or on an agent-raised decision) offers the button that
|
|
10
|
+
// opens the window resolving it, instead of leaving the card on a spinning "Run
|
|
11
|
+
// planning" whose only route in was the inspector's execution panel.
|
|
8
12
|
// The tracker button opens the dedicated window directly. Draggable within its
|
|
9
13
|
// frame like a task card.
|
|
10
14
|
import type { InitiativeStatus } from '~/types/domain'
|
|
11
15
|
import { useBlockDrag } from '~/composables/useBlockDrag'
|
|
12
16
|
import { useInitiativePlanning } from '~/composables/useInitiativePlanning'
|
|
13
17
|
import {
|
|
18
|
+
INITIATIVE_ATTENTION_ICONS,
|
|
19
|
+
INITIATIVE_ATTENTION_LABEL_KEYS,
|
|
14
20
|
INITIATIVE_STATUS_CHIPS,
|
|
15
21
|
INITIATIVE_STATUS_LABEL_KEYS,
|
|
16
22
|
initiativeProgress,
|
|
@@ -40,6 +46,7 @@ const {
|
|
|
40
46
|
running,
|
|
41
47
|
awaitingAnswers,
|
|
42
48
|
interviewing,
|
|
49
|
+
attention,
|
|
43
50
|
starting,
|
|
44
51
|
runPlanning,
|
|
45
52
|
openPlanning,
|
|
@@ -79,7 +86,10 @@ function onHandle(e: PointerEvent) {
|
|
|
79
86
|
data-testid="initiative-card"
|
|
80
87
|
:data-status="status"
|
|
81
88
|
class="cursor-pointer rounded-b-lg border border-indigo-800/60 bg-indigo-950/40 p-3 transition hover:border-indigo-600"
|
|
82
|
-
:class="[
|
|
89
|
+
:class="[
|
|
90
|
+
selected ? 'ring-2 ring-indigo-400/60' : '',
|
|
91
|
+
awaitingAnswers || attention ? 'board-pulse' : '',
|
|
92
|
+
]"
|
|
83
93
|
@click.stop="select"
|
|
84
94
|
>
|
|
85
95
|
<div class="flex items-start justify-between gap-2">
|
|
@@ -106,8 +116,23 @@ function onHandle(e: PointerEvent) {
|
|
|
106
116
|
</div>
|
|
107
117
|
</div>
|
|
108
118
|
<div class="nodrag mt-2 flex flex-wrap items-center gap-1">
|
|
119
|
+
<!-- Parked for a human: the drafted plan awaits approval, or an agent raised a
|
|
120
|
+
decision. Opens the window that can resolve the park (never the generic panel,
|
|
121
|
+
which the server refuses for a park a dedicated window owns). -->
|
|
109
122
|
<UButton
|
|
110
|
-
v-if="
|
|
123
|
+
v-if="attention"
|
|
124
|
+
data-testid="initiative-card-review"
|
|
125
|
+
:data-attention="attention.kind"
|
|
126
|
+
size="xs"
|
|
127
|
+
variant="solid"
|
|
128
|
+
color="warning"
|
|
129
|
+
:icon="INITIATIVE_ATTENTION_ICONS[attention.kind]"
|
|
130
|
+
@click.stop="attention.open()"
|
|
131
|
+
>
|
|
132
|
+
{{ t(INITIATIVE_ATTENTION_LABEL_KEYS[attention.kind]) }}
|
|
133
|
+
</UButton>
|
|
134
|
+
<UButton
|
|
135
|
+
v-else-if="awaitingAnswers"
|
|
111
136
|
data-testid="initiative-card-answer-planning"
|
|
112
137
|
size="xs"
|
|
113
138
|
variant="solid"
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
// The initiative tracker window — the dedicated
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
2
|
+
// The initiative tracker window — the dedicated view of an initiative's plan/tracker
|
|
3
|
+
// entity: goal + constraints, the phases with their per-item status + PR links, the
|
|
4
|
+
// execution policy, and the decisions / deviations / follow-ups / caveats logs.
|
|
5
|
+
// Renders the DB entity (the source of truth) — never the in-repo mirror, which may
|
|
6
|
+
// not exist (GitHub-unwired workspaces). Opened via the universal result-view host:
|
|
7
|
+
// from the board card / inspector (`ui.openInitiativeTracker`) or as the planner
|
|
8
|
+
// step's result view. Live `initiative` stream events patch the store, so an open
|
|
9
|
+
// window follows the plan as it is ingested and later executed.
|
|
10
|
+
//
|
|
11
|
+
// It also OWNS the planner's plan-approval gate: this window is where the planner
|
|
12
|
+
// step's park routes (its archetype declares this result view), so the approve /
|
|
13
|
+
// request-changes rail has to live here or the gate has no resolving surface at all —
|
|
14
|
+
// which is exactly how an approved-only-over-REST plan gate shipped.
|
|
15
|
+
import { computed, reactive, ref, watch } from 'vue'
|
|
11
16
|
import type { InitiativeFollowUp, InitiativeItem } from '~/types/domain'
|
|
17
|
+
import { useInitiativePlanning } from '~/composables/useInitiativePlanning'
|
|
12
18
|
import {
|
|
13
19
|
INITIATIVE_FOLLOWUP_STATUS_CHIPS,
|
|
14
20
|
INITIATIVE_FOLLOWUP_STATUS_LABEL_KEYS,
|
|
@@ -23,6 +29,8 @@ import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
|
23
29
|
|
|
24
30
|
const board = useBoardStore()
|
|
25
31
|
const initiatives = useInitiativesStore()
|
|
32
|
+
const execution = useExecutionStore()
|
|
33
|
+
const access = useWorkspaceAccess()
|
|
26
34
|
const { t } = useI18n()
|
|
27
35
|
const toast = useToast()
|
|
28
36
|
|
|
@@ -92,6 +100,58 @@ async function checkpointControl(action: 'resume' | 'cancel') {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
103
|
+
// ---- Plan review: the planner step's human gate, resolved right here -----------------------
|
|
104
|
+
// Derived from the BLOCK (via the shared planning composable), not from this window's own
|
|
105
|
+
// `stepIndex`: the card / inspector open the tracker with no step, and that is the entry point a
|
|
106
|
+
// human parked on the gate actually uses. So the rail appears on every route into the window.
|
|
107
|
+
const { planApproval } = useInitiativePlanning(() => blockId.value ?? '')
|
|
108
|
+
|
|
109
|
+
/** Draft feedback for "request changes" (the planner re-runs with it), and the rail's in-flight
|
|
110
|
+
* state. Reset when a different initiative opens so a draft can't follow the window. */
|
|
111
|
+
const planFeedback = ref('')
|
|
112
|
+
const requestingChanges = ref(false)
|
|
113
|
+
const resolvingPlan = ref(false)
|
|
114
|
+
watch(blockId, () => {
|
|
115
|
+
planFeedback.value = ''
|
|
116
|
+
requestingChanges.value = false
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const canRequestChanges = computed(() => planFeedback.value.trim().length > 0)
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Accept the drafted plan: the run advances to the committer, which persists the initiative and
|
|
123
|
+
* arms the execution loop. The window deliberately stays OPEN — the rail disappears with the
|
|
124
|
+
* approval (live), and the tracker is where the plan then starts executing.
|
|
125
|
+
*/
|
|
126
|
+
async function approvePlan() {
|
|
127
|
+
const parked = planApproval.value
|
|
128
|
+
if (!parked || resolvingPlan.value) return
|
|
129
|
+
resolvingPlan.value = true
|
|
130
|
+
try {
|
|
131
|
+
await execution.approveStep(parked.instanceId, parked.approval.id)
|
|
132
|
+
} finally {
|
|
133
|
+
resolvingPlan.value = false
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Send the plan back to the planner with what to change; it re-plans and parks again. */
|
|
138
|
+
async function submitPlanChanges() {
|
|
139
|
+
const parked = planApproval.value
|
|
140
|
+
if (!parked || resolvingPlan.value || !canRequestChanges.value) return
|
|
141
|
+
resolvingPlan.value = true
|
|
142
|
+
try {
|
|
143
|
+
const ok = await execution.requestStepChanges(parked.instanceId, parked.approval.id, {
|
|
144
|
+
feedback: planFeedback.value.trim(),
|
|
145
|
+
})
|
|
146
|
+
if (ok) {
|
|
147
|
+
planFeedback.value = ''
|
|
148
|
+
requestingChanges.value = false
|
|
149
|
+
}
|
|
150
|
+
} finally {
|
|
151
|
+
resolvingPlan.value = false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
95
155
|
const policyRules = computed(() => initiative.value?.policy?.rules ?? [])
|
|
96
156
|
function ruleAxes(rule: { minComplexity?: number; minRisk?: number; minImpact?: number }): string {
|
|
97
157
|
const axes = [
|
|
@@ -234,6 +294,86 @@ async function savePolicy() {
|
|
|
234
294
|
</div>
|
|
235
295
|
|
|
236
296
|
<template v-else>
|
|
297
|
+
<!-- The planner's human gate: the plan below is drafted but NOT yet committed. This
|
|
298
|
+
is the only surface that can resolve it (the generic approval panel is never
|
|
299
|
+
reached — the planner's archetype routes its park to this window), so approve /
|
|
300
|
+
request changes live here, beside the plan they judge. -->
|
|
301
|
+
<section
|
|
302
|
+
v-if="planApproval"
|
|
303
|
+
class="mb-4 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3.5"
|
|
304
|
+
data-testid="initiative-plan-review"
|
|
305
|
+
>
|
|
306
|
+
<div class="flex items-start gap-2.5">
|
|
307
|
+
<UIcon
|
|
308
|
+
name="i-lucide-clipboard-check"
|
|
309
|
+
class="mt-0.5 h-4 w-4 shrink-0 text-amber-300"
|
|
310
|
+
/>
|
|
311
|
+
<div class="min-w-0 flex-1">
|
|
312
|
+
<h3 class="text-[13px] font-semibold text-amber-200">
|
|
313
|
+
{{ t('initiative.planReview.title') }}
|
|
314
|
+
</h3>
|
|
315
|
+
<p class="mt-0.5 text-[12px] leading-relaxed text-amber-100/80">
|
|
316
|
+
{{ t('initiative.planReview.body') }}
|
|
317
|
+
</p>
|
|
318
|
+
<div v-if="!requestingChanges" class="mt-2.5 flex flex-wrap gap-2">
|
|
319
|
+
<button
|
|
320
|
+
class="rounded bg-indigo-600 px-2.5 py-1 text-[11px] font-medium text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
321
|
+
:disabled="resolvingPlan || !access.canExecuteRuns.value"
|
|
322
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
323
|
+
data-testid="initiative-plan-approve"
|
|
324
|
+
@click="approvePlan"
|
|
325
|
+
>
|
|
326
|
+
{{ t('initiative.planReview.approve') }}
|
|
327
|
+
</button>
|
|
328
|
+
<button
|
|
329
|
+
class="rounded border border-amber-400/50 px-2.5 py-1 text-[11px] font-medium text-amber-200 hover:bg-amber-500/10 disabled:opacity-50"
|
|
330
|
+
:disabled="resolvingPlan || !access.canExecuteRuns.value"
|
|
331
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
332
|
+
data-testid="initiative-plan-request-changes"
|
|
333
|
+
@click="requestingChanges = true"
|
|
334
|
+
>
|
|
335
|
+
{{ t('initiative.planReview.requestChanges') }}
|
|
336
|
+
</button>
|
|
337
|
+
</div>
|
|
338
|
+
<!-- Request-changes composer: the feedback is what the planner re-plans FROM,
|
|
339
|
+
so it is required — an empty send would re-run the planner with nothing
|
|
340
|
+
to act on and park again on the same plan. -->
|
|
341
|
+
<div v-else class="mt-2.5">
|
|
342
|
+
<UTextarea
|
|
343
|
+
v-model="planFeedback"
|
|
344
|
+
:rows="3"
|
|
345
|
+
autoresize
|
|
346
|
+
size="sm"
|
|
347
|
+
class="w-full"
|
|
348
|
+
data-testid="initiative-plan-feedback"
|
|
349
|
+
:placeholder="t('initiative.planReview.feedbackPlaceholder')"
|
|
350
|
+
/>
|
|
351
|
+
<div class="mt-2 flex flex-wrap gap-2">
|
|
352
|
+
<button
|
|
353
|
+
class="rounded bg-amber-500 px-2.5 py-1 text-[11px] font-medium text-slate-950 hover:bg-amber-400 disabled:opacity-50"
|
|
354
|
+
:disabled="
|
|
355
|
+
resolvingPlan || !canRequestChanges || !access.canExecuteRuns.value
|
|
356
|
+
"
|
|
357
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
358
|
+
data-testid="initiative-plan-send-back"
|
|
359
|
+
@click="submitPlanChanges"
|
|
360
|
+
>
|
|
361
|
+
{{ t('initiative.planReview.sendBack') }}
|
|
362
|
+
</button>
|
|
363
|
+
<button
|
|
364
|
+
class="rounded border border-slate-600 px-2.5 py-1 text-[11px] font-medium text-slate-300 hover:bg-slate-800 disabled:opacity-50"
|
|
365
|
+
:disabled="resolvingPlan"
|
|
366
|
+
data-testid="initiative-plan-cancel-changes"
|
|
367
|
+
@click="requestingChanges = false"
|
|
368
|
+
>
|
|
369
|
+
{{ t('common.cancel') }}
|
|
370
|
+
</button>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</section>
|
|
376
|
+
|
|
237
377
|
<!-- Paused at a phase checkpoint (D2): a completed checkpoint phase is awaiting
|
|
238
378
|
review before the next phase spawns. Read the phase's artifacts/PRs below,
|
|
239
379
|
then resume (continue) or cancel (stop) the initiative right here. -->
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
// with slice 4.
|
|
7
7
|
import type { Block, InitiativeStatus } from '~/types/domain'
|
|
8
8
|
import { useInitiativePlanning } from '~/composables/useInitiativePlanning'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
INITIATIVE_ATTENTION_ICONS,
|
|
11
|
+
INITIATIVE_ATTENTION_LABEL_KEYS,
|
|
12
|
+
INITIATIVE_STATUS_LABEL_KEYS,
|
|
13
|
+
initiativeProgress,
|
|
14
|
+
} from '~/utils/initiative'
|
|
10
15
|
|
|
11
16
|
const props = defineProps<{ block: Block }>()
|
|
12
17
|
|
|
@@ -24,6 +29,7 @@ const {
|
|
|
24
29
|
running,
|
|
25
30
|
awaitingAnswers,
|
|
26
31
|
interviewing,
|
|
32
|
+
attention,
|
|
27
33
|
starting,
|
|
28
34
|
runPlanning,
|
|
29
35
|
openPlanning,
|
|
@@ -56,8 +62,23 @@ function control(action: 'pause' | 'resume' | 'cancel') {
|
|
|
56
62
|
</p>
|
|
57
63
|
|
|
58
64
|
<div class="flex flex-wrap items-center gap-2">
|
|
65
|
+
<!-- Parked for a human (the drafted plan awaits approval, or an agent raised a decision).
|
|
66
|
+
The same affordance the board card carries, resolved from the same composable — the
|
|
67
|
+
run's park must not be reachable only through the execution panel's step list. -->
|
|
59
68
|
<UButton
|
|
60
|
-
v-if="
|
|
69
|
+
v-if="attention"
|
|
70
|
+
data-testid="initiative-review"
|
|
71
|
+
:data-attention="attention.kind"
|
|
72
|
+
color="warning"
|
|
73
|
+
variant="solid"
|
|
74
|
+
size="sm"
|
|
75
|
+
:icon="INITIATIVE_ATTENTION_ICONS[attention.kind]"
|
|
76
|
+
@click="attention.open()"
|
|
77
|
+
>
|
|
78
|
+
{{ t(INITIATIVE_ATTENTION_LABEL_KEYS[attention.kind]) }}
|
|
79
|
+
</UButton>
|
|
80
|
+
<UButton
|
|
81
|
+
v-else-if="awaitingAnswers"
|
|
61
82
|
data-testid="initiative-answer-planning"
|
|
62
83
|
color="primary"
|
|
63
84
|
variant="solid"
|
|
@@ -4,12 +4,25 @@ import { useExecutionStore } from '~/stores/execution'
|
|
|
4
4
|
import { useInitiativesStore } from '~/stores/initiative'
|
|
5
5
|
import { usePipelinesStore } from '~/stores/pipelines'
|
|
6
6
|
import { useUiStore } from '~/stores/ui'
|
|
7
|
+
import { agentKindMeta } from '~/utils/catalog'
|
|
8
|
+
import { selectPlanApproval, type InitiativeAttentionKind } from '~/utils/initiative'
|
|
7
9
|
import {
|
|
8
10
|
INITIATIVE_INTERVIEWER_KIND,
|
|
9
11
|
interviewGatePhase,
|
|
10
12
|
interviewStepReached,
|
|
11
13
|
} from '~/utils/interviewGate'
|
|
12
14
|
|
|
15
|
+
/**
|
|
16
|
+
* What an initiative's planning run needs from a human right now: which kind of park it is (the
|
|
17
|
+
* card + inspector resolve its icon/label from the shared `INITIATIVE_ATTENTION_*` maps, so the
|
|
18
|
+
* two surfaces word one park identically), and the action that opens the surface which can
|
|
19
|
+
* RESOLVE it — the step's own dedicated window, via `dispatchStepView`.
|
|
20
|
+
*/
|
|
21
|
+
export interface InitiativeAttention {
|
|
22
|
+
kind: InitiativeAttentionKind
|
|
23
|
+
open: () => void
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
/**
|
|
14
27
|
* Shared planning affordances for an `initiative`-level block, used by BOTH the board card
|
|
15
28
|
* (`InitiativeCard`) and the inspector (`InitiativeInspector`) so the two surfaces can never drift
|
|
@@ -83,6 +96,60 @@ export function useInitiativePlanning(blockId: MaybeRefOrGetter<string>) {
|
|
|
83
96
|
() => interviewPhase.value === 'working' || interviewPhase.value === 'preparing',
|
|
84
97
|
)
|
|
85
98
|
|
|
99
|
+
/**
|
|
100
|
+
* The planner's parked plan-approval gate, or undefined. `pl_initiative` gates the planner step
|
|
101
|
+
* (`{ kind: 'initiative-planner', gate: true }`), so a finished planning pass PARKS the run on a
|
|
102
|
+
* pending `step.approval` until a human accepts the plan — the state this composable's other
|
|
103
|
+
* flags deliberately do NOT cover (the interview has converged, and the run is `blocked`, so
|
|
104
|
+
* `awaitingAnswers` and `interviewing` are both false and the card would otherwise sit on a
|
|
105
|
+
* disabled, spinning "Run planning").
|
|
106
|
+
*
|
|
107
|
+
* The interviewer's own park is excluded by the window its step routes to (see
|
|
108
|
+
* {@link selectPlanApproval}), not by the interview phase, so the two affordances can never both
|
|
109
|
+
* claim one park.
|
|
110
|
+
*/
|
|
111
|
+
const planApproval = computed(() =>
|
|
112
|
+
selectPlanApproval(
|
|
113
|
+
execution.approvalsByBlock.get(toValue(blockId)) ?? [],
|
|
114
|
+
(kind) => agentKindMeta(kind).resultView,
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
/** An agent-raised decision on the planning run (the analyst is an ordinary agent step). */
|
|
119
|
+
const pendingDecision = computed(() => execution.decisionsByBlock.get(toValue(blockId))?.[0])
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The single thing a human has to act on, or null — the initiative dual of a task card's
|
|
123
|
+
* `attention`. A decision outranks an approval (a step never holds both; this is just a stable
|
|
124
|
+
* order). Opening always goes through the step-view dispatch, so the park lands in the window
|
|
125
|
+
* that can RESOLVE it (the plan gate → the tracker window's plan-review rail) rather than a
|
|
126
|
+
* generic panel that would refuse it.
|
|
127
|
+
*/
|
|
128
|
+
const attention = computed<InitiativeAttention | null>(() => {
|
|
129
|
+
const id = toValue(blockId)
|
|
130
|
+
const decision = pendingDecision.value
|
|
131
|
+
if (decision) {
|
|
132
|
+
return {
|
|
133
|
+
kind: 'decision',
|
|
134
|
+
open: () => {
|
|
135
|
+
ui.select(id)
|
|
136
|
+
ui.openDecision(decision.instanceId, decision.decision.id)
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const approval = planApproval.value
|
|
141
|
+
if (approval) {
|
|
142
|
+
return {
|
|
143
|
+
kind: 'approval',
|
|
144
|
+
open: () => {
|
|
145
|
+
ui.select(id)
|
|
146
|
+
ui.openApprovalDetail(approval.instanceId, approval.approval.id)
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null
|
|
151
|
+
})
|
|
152
|
+
|
|
86
153
|
/**
|
|
87
154
|
* Optimistic start flag: flip true the instant "Run planning" is clicked, before the stream
|
|
88
155
|
* pushes the block's `executionId` back. Cleared the moment `running` takes over (success) or the
|
|
@@ -123,6 +190,9 @@ export function useInitiativePlanning(blockId: MaybeRefOrGetter<string>) {
|
|
|
123
190
|
interviewPhase,
|
|
124
191
|
awaitingAnswers,
|
|
125
192
|
interviewing,
|
|
193
|
+
planApproval,
|
|
194
|
+
pendingDecision,
|
|
195
|
+
attention,
|
|
126
196
|
starting,
|
|
127
197
|
runPlanning,
|
|
128
198
|
openPlanning,
|
|
@@ -285,10 +285,20 @@ describe('nav grouping helpers', () => {
|
|
|
285
285
|
'workspaceContext',
|
|
286
286
|
'configuration',
|
|
287
287
|
])
|
|
288
|
-
// The
|
|
289
|
-
//
|
|
288
|
+
// The model layer is its own section, ahead of the optional integrations: the engines,
|
|
289
|
+
// the per-agent model choice (beside the providers it picks from, rather than under
|
|
290
|
+
// `configuration`), and the two surfaces that evaluate a prompt+agent+model. Sandbox and
|
|
291
|
+
// Kaizen used to sit under `integrations`, which read as a claim they connect to an
|
|
292
|
+
// external system; they don't — they exercise and grade what this section configures.
|
|
290
293
|
const models = groups.find((g) => g.group === 'models')
|
|
291
|
-
expect(models?.items.map((i) => i.id)).toEqual([
|
|
294
|
+
expect(models?.items.map((i) => i.id)).toEqual([
|
|
295
|
+
'model-providers',
|
|
296
|
+
'model-config',
|
|
297
|
+
'sandbox',
|
|
298
|
+
'kaizen',
|
|
299
|
+
])
|
|
300
|
+
const integrations = groups.find((g) => g.group === 'integrations')
|
|
301
|
+
expect(integrations?.items.map((i) => i.id)).toEqual(['integrations-hub'])
|
|
292
302
|
const configuration = groups.find((g) => g.group === 'configuration')
|
|
293
303
|
expect(configuration?.items.map((i) => i.id)).toEqual([
|
|
294
304
|
'workspace-settings',
|
|
@@ -30,11 +30,15 @@ export type NavSurface = 'sidebar' | 'command' | 'toolbar'
|
|
|
30
30
|
*
|
|
31
31
|
* `models` and `integrations` are deliberately SEPARATE sections even though a model
|
|
32
32
|
* provider is technically also an external system we connect to. They answer different
|
|
33
|
-
* questions: `models` is the
|
|
34
|
-
* all),
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
33
|
+
* questions: `models` is the MODEL LAYER — which engine the harnesses run on (no provider
|
|
34
|
+
* ⇒ nothing runs at all), which model each agent kind uses, and how well a given
|
|
35
|
+
* prompt+agent+model actually performs (`sandbox`, `kaizen`); `integrations` is the
|
|
36
|
+
* optional EXTERNAL SYSTEMS that feed a run context or receive its output (source control,
|
|
37
|
+
* trackers, documents, chat, observability) — each of which a deployment can live without.
|
|
38
|
+
* Folding the providers in among them buried the one connection every deployment must make
|
|
39
|
+
* in a list of ones most never touch; parking the two model-quality surfaces there was the
|
|
40
|
+
* same mistake from the other end — neither Sandbox nor Kaizen connects to anything, they
|
|
41
|
+
* evaluate what the `models` section configures.
|
|
38
42
|
*/
|
|
39
43
|
export type NavSidebarGroup =
|
|
40
44
|
| 'create'
|
|
@@ -268,33 +272,6 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
|
|
|
268
272
|
testId: 'nav-integrations',
|
|
269
273
|
sidebar: { group: 'integrations', order: 10 },
|
|
270
274
|
},
|
|
271
|
-
{
|
|
272
|
-
id: 'sandbox',
|
|
273
|
-
labelKey: 'nav.sandbox',
|
|
274
|
-
icon: 'i-lucide-flask-conical',
|
|
275
|
-
surfaces: S('sidebar', 'command'),
|
|
276
|
-
advanced: true,
|
|
277
|
-
gate: (g) => g.canManageIntegrations,
|
|
278
|
-
action: 'sandbox',
|
|
279
|
-
testId: 'nav-sandbox',
|
|
280
|
-
sidebar: { group: 'integrations', order: 20 },
|
|
281
|
-
command: {
|
|
282
|
-
group: 'workspace',
|
|
283
|
-
order: 70,
|
|
284
|
-
labelKey: 'layout.commandBar.cmd.sandbox',
|
|
285
|
-
keywordsKey: 'layout.commandBar.keywords.sandbox',
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
id: 'kaizen',
|
|
290
|
-
labelKey: 'nav.kaizen',
|
|
291
|
-
icon: 'i-lucide-sparkles',
|
|
292
|
-
surfaces: S('sidebar'),
|
|
293
|
-
advanced: true,
|
|
294
|
-
action: 'kaizen',
|
|
295
|
-
testId: 'nav-kaizen',
|
|
296
|
-
sidebar: { group: 'integrations', order: 30 },
|
|
297
|
-
},
|
|
298
275
|
{
|
|
299
276
|
id: 'infrastructure',
|
|
300
277
|
labelKey: 'nav.infrastructure',
|
|
@@ -370,6 +347,39 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
|
|
|
370
347
|
keywordsKey: 'layout.commandBar.keywords.modelConfiguration',
|
|
371
348
|
},
|
|
372
349
|
},
|
|
350
|
+
{
|
|
351
|
+
// Trying prompt versions and models against graded fixtures — a model-layer surface,
|
|
352
|
+
// not an integration: it connects to no external system, it exercises the providers
|
|
353
|
+
// and per-agent models the two entries above configure.
|
|
354
|
+
id: 'sandbox',
|
|
355
|
+
labelKey: 'nav.sandbox',
|
|
356
|
+
icon: 'i-lucide-flask-conical',
|
|
357
|
+
surfaces: S('sidebar', 'command'),
|
|
358
|
+
advanced: true,
|
|
359
|
+
gate: (g) => g.canManageIntegrations,
|
|
360
|
+
action: 'sandbox',
|
|
361
|
+
testId: 'nav-sandbox',
|
|
362
|
+
sidebar: { group: 'models', order: 30 },
|
|
363
|
+
command: {
|
|
364
|
+
group: 'workspace',
|
|
365
|
+
order: 70,
|
|
366
|
+
labelKey: 'layout.commandBar.cmd.sandbox',
|
|
367
|
+
keywordsKey: 'layout.commandBar.keywords.sandbox',
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
// The same axis after the fact: grading history and verified prompt+agent+model combos.
|
|
372
|
+
// Sandbox asks "which combination should we use", Kaizen answers "how is the one we
|
|
373
|
+
// shipped doing" — both read the model layer, so they sit with it.
|
|
374
|
+
id: 'kaizen',
|
|
375
|
+
labelKey: 'nav.kaizen',
|
|
376
|
+
icon: 'i-lucide-sparkles',
|
|
377
|
+
surfaces: S('sidebar'),
|
|
378
|
+
advanced: true,
|
|
379
|
+
action: 'kaizen',
|
|
380
|
+
testId: 'nav-kaizen',
|
|
381
|
+
sidebar: { group: 'models', order: 40 },
|
|
382
|
+
},
|
|
373
383
|
{
|
|
374
384
|
id: 'service-fragment-defaults',
|
|
375
385
|
labelKey: 'layout.commandBar.cmd.serviceFragmentDefaults',
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { INITIATIVE_ITEM_TERMINAL_STATUSES } from '@cat-factory/contracts'
|
|
2
2
|
import { describe, it, expect } from 'vitest'
|
|
3
3
|
import type { InitiativeItem, InitiativePhase, InitiativeQa } from '~/types/domain'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
isPendingQuestion,
|
|
6
|
+
orderInterviewQuestions,
|
|
7
|
+
pendingCheckpointPhase,
|
|
8
|
+
selectPlanApproval,
|
|
9
|
+
} from './initiative'
|
|
5
10
|
|
|
6
11
|
// `pendingCheckpointPhase` mirrors the backend `pendingCheckpoint` (orchestration
|
|
7
12
|
// `initiative.logic.ts`); these pin the same ordering/edge cases the loop pauses on, so the
|
|
@@ -150,3 +155,48 @@ describe('orderInterviewQuestions', () => {
|
|
|
150
155
|
expect(orderInterviewQuestions([])).toEqual([])
|
|
151
156
|
})
|
|
152
157
|
})
|
|
158
|
+
|
|
159
|
+
// The plan-review park: which of a block's pending approvals the board card / inspector offer as
|
|
160
|
+
// "Review plan", and which one they must leave alone. Both gates on the planning pipeline park on
|
|
161
|
+
// a `step.approval`, so the interviewer's park (owned by the planning window's "Answer planning
|
|
162
|
+
// questions") is the case this selector exists to keep out — offering it here would give one park
|
|
163
|
+
// two differently-worded buttons, and the tracker window it opened could not resolve it.
|
|
164
|
+
|
|
165
|
+
/** A pending approval as `execution.approvalsByBlock` carries it (only the fields read here). */
|
|
166
|
+
const parked = (agentKind: string, id: string) => ({ agentKind, approval: { id } })
|
|
167
|
+
|
|
168
|
+
/** The catalog's result-view resolver, as the composable passes it in. */
|
|
169
|
+
const resultViewOf = (kind: string): string | undefined =>
|
|
170
|
+
kind === 'initiative-interviewer'
|
|
171
|
+
? 'initiative-planning'
|
|
172
|
+
: kind.startsWith('initiative-')
|
|
173
|
+
? 'initiative-tracker'
|
|
174
|
+
: undefined
|
|
175
|
+
|
|
176
|
+
describe('selectPlanApproval', () => {
|
|
177
|
+
it('is undefined when nothing is parked', () => {
|
|
178
|
+
expect(selectPlanApproval([], resultViewOf)).toBeUndefined()
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('picks the planner gate — the plan awaiting approval', () => {
|
|
182
|
+
const approvals = [parked('initiative-planner', 'ap_1')]
|
|
183
|
+
expect(selectPlanApproval(approvals, resultViewOf)?.approval.id).toBe('ap_1')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('leaves the interviewer park to the planning window', () => {
|
|
187
|
+
const approvals = [parked('initiative-interviewer', 'ap_interview')]
|
|
188
|
+
expect(selectPlanApproval(approvals, resultViewOf)).toBeUndefined()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('finds the plan gate past an interview park (a re-run interviewing again)', () => {
|
|
192
|
+
const approvals = [parked('initiative-interviewer', 'ap_interview'), parked('x', 'ap_plan')]
|
|
193
|
+
expect(selectPlanApproval(approvals, resultViewOf)?.approval.id).toBe('ap_plan')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('offers a gated step of a custom planning pipeline, whatever window it routes to', () => {
|
|
197
|
+
// A kind with no dedicated window at all still parks a human; the affordance opens whatever
|
|
198
|
+
// `dispatchStepView` routes it to (the generic panel), which is exactly what resolves it.
|
|
199
|
+
const approvals = [parked('some-custom-kind', 'ap_custom')]
|
|
200
|
+
expect(selectPlanApproval(approvals, resultViewOf)?.approval.id).toBe('ap_custom')
|
|
201
|
+
})
|
|
202
|
+
})
|
package/app/utils/initiative.ts
CHANGED
|
@@ -59,6 +59,50 @@ export const INITIATIVE_ITEM_STATUS_CHIPS: Record<InitiativeItemStatus, BadgeCol
|
|
|
59
59
|
skipped: 'neutral',
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The two ways an initiative's planning run parks for a human: an agent-raised `decision`, or a
|
|
64
|
+
* pending step `approval` (the plan-approval gate the `initiative-planner` step carries). Resolved
|
|
65
|
+
* per block by `useInitiativePlanning().attention`.
|
|
66
|
+
*/
|
|
67
|
+
export type InitiativeAttentionKind = 'decision' | 'approval'
|
|
68
|
+
|
|
69
|
+
/** Park kind → i18n label key for the card/inspector button that opens the resolving window. */
|
|
70
|
+
export const INITIATIVE_ATTENTION_LABEL_KEYS: Record<InitiativeAttentionKind, string> = {
|
|
71
|
+
decision: 'initiative.inspector.resolveDecision',
|
|
72
|
+
approval: 'initiative.inspector.reviewPlan',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Park kind → button icon, so the board card and the inspector can't diverge on one park. */
|
|
76
|
+
export const INITIATIVE_ATTENTION_ICONS: Record<InitiativeAttentionKind, string> = {
|
|
77
|
+
decision: 'i-lucide-circle-help',
|
|
78
|
+
approval: 'i-lucide-clipboard-check',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The result view the INTERVIEW gate owns. Its park rides the same `step.approval` mechanism as
|
|
83
|
+
* every other gate, so anything offering "there is a plan to review here" has to exclude it: the
|
|
84
|
+
* interview park is already owned by the planning window, behind the differently-worded "Answer
|
|
85
|
+
* planning questions".
|
|
86
|
+
*/
|
|
87
|
+
export const INTERVIEW_GATE_RESULT_VIEW = 'initiative-planning'
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The block's parked approval that is a PLAN REVIEW — the planner's human gate (`pl_initiative`
|
|
91
|
+
* declares `{ kind: 'initiative-planner', gate: true }`), or any other gated step of a custom
|
|
92
|
+
* planning pipeline — as opposed to the interviewer's park.
|
|
93
|
+
*
|
|
94
|
+
* Discriminated by the step's own result view, the seam `dispatchStepView` routes on, rather than
|
|
95
|
+
* by an agent-kind list or by the interview phase: the affordance's ACTION is that dispatch, so
|
|
96
|
+
* keying the offer on the same fact guarantees the button opens a window that can resolve what it
|
|
97
|
+
* offered — and that the interview and plan affordances can never both claim one park.
|
|
98
|
+
*/
|
|
99
|
+
export function selectPlanApproval<A extends { agentKind: string }>(
|
|
100
|
+
approvals: readonly A[],
|
|
101
|
+
resultViewOf: (agentKind: string) => string | undefined,
|
|
102
|
+
): A | undefined {
|
|
103
|
+
return approvals.find((a) => resultViewOf(a.agentKind) !== INTERVIEW_GATE_RESULT_VIEW)
|
|
104
|
+
}
|
|
105
|
+
|
|
62
106
|
/** Follow-up triage status → i18n label key. Exhaustive so a new status fails the build. */
|
|
63
107
|
export const INITIATIVE_FOLLOWUP_STATUS_LABEL_KEYS: Record<InitiativeFollowUp['status'], string> = {
|
|
64
108
|
open: 'initiative.followUpStatus.open',
|
package/i18n/locales/de.json
CHANGED
|
@@ -4282,12 +4282,22 @@
|
|
|
4282
4282
|
"inspector": {
|
|
4283
4283
|
"runPlanning": "Planung ausführen",
|
|
4284
4284
|
"answerPlanning": "Planungsfragen beantworten",
|
|
4285
|
+
"reviewPlan": "Plan prüfen",
|
|
4286
|
+
"resolveDecision": "Auflösen",
|
|
4285
4287
|
"planningInProgress": "Planung läuft",
|
|
4286
4288
|
"pause": "Pausieren",
|
|
4287
4289
|
"resume": "Fortsetzen",
|
|
4288
4290
|
"cancel": "Initiative abbrechen",
|
|
4289
4291
|
"hint": "Die Planning-Pipeline erkundet die Codebasis, entwirft den mehrphasigen Plan zur Freigabe und committet dann das Tracker-Dokument ins Repository."
|
|
4290
4292
|
},
|
|
4293
|
+
"planReview": {
|
|
4294
|
+
"title": "Dieser Plan wartet auf dich",
|
|
4295
|
+
"body": "Der Planner hat die Phasen und Aufgaben unten entworfen. Gib sie frei, um den Plan zu committen und die Arbeit zu starten, oder schicke den Plan mit deinen Änderungswünschen zurück.",
|
|
4296
|
+
"approve": "Plan freigeben",
|
|
4297
|
+
"requestChanges": "Änderungen anfordern",
|
|
4298
|
+
"feedbackPlaceholder": "Was soll der Planner ändern? Umfang, Reihenfolge der Phasen, fehlende Arbeit, eine Aufgabe, die woanders hingehört …",
|
|
4299
|
+
"sendBack": "An den Planner zurückschicken"
|
|
4300
|
+
},
|
|
4291
4301
|
"planning": {
|
|
4292
4302
|
"title": "Die Initiative planen",
|
|
4293
4303
|
"subtitle": "Beantworten Sie die Fragen des Planers, damit er die Initiative eingrenzen kann",
|
package/i18n/locales/en.json
CHANGED
|
@@ -5459,12 +5459,22 @@
|
|
|
5459
5459
|
"inspector": {
|
|
5460
5460
|
"runPlanning": "Run planning",
|
|
5461
5461
|
"answerPlanning": "Answer planning questions",
|
|
5462
|
+
"reviewPlan": "Review plan",
|
|
5463
|
+
"resolveDecision": "Resolve",
|
|
5462
5464
|
"planningInProgress": "Planning in progress",
|
|
5463
5465
|
"pause": "Pause",
|
|
5464
5466
|
"resume": "Resume",
|
|
5465
5467
|
"cancel": "Cancel initiative",
|
|
5466
5468
|
"hint": "The planning pipeline explores the codebase, drafts the multi-phase plan for approval, then commits the tracker document to the repository."
|
|
5467
5469
|
},
|
|
5470
|
+
"planReview": {
|
|
5471
|
+
"title": "This plan is waiting for you",
|
|
5472
|
+
"body": "The planner drafted the phases and items below. Approve them to commit the plan and start the work, or send the plan back with what to change.",
|
|
5473
|
+
"approve": "Approve plan",
|
|
5474
|
+
"requestChanges": "Request changes",
|
|
5475
|
+
"feedbackPlaceholder": "What should the planner change? Scope, phase order, missing work, an item that belongs elsewhere…",
|
|
5476
|
+
"sendBack": "Send back to the planner"
|
|
5477
|
+
},
|
|
5468
5478
|
"planning": {
|
|
5469
5479
|
"title": "Plan the initiative",
|
|
5470
5480
|
"subtitle": "Answer the planner's questions so it can scope the initiative",
|
package/i18n/locales/es.json
CHANGED
|
@@ -5294,12 +5294,22 @@
|
|
|
5294
5294
|
"inspector": {
|
|
5295
5295
|
"runPlanning": "Ejecutar planificacion",
|
|
5296
5296
|
"answerPlanning": "Responder preguntas de planificacion",
|
|
5297
|
+
"reviewPlan": "Revisar plan",
|
|
5298
|
+
"resolveDecision": "Resolver",
|
|
5297
5299
|
"planningInProgress": "Planificacion en curso",
|
|
5298
5300
|
"pause": "Pausar",
|
|
5299
5301
|
"resume": "Reanudar",
|
|
5300
5302
|
"cancel": "Cancelar iniciativa",
|
|
5301
5303
|
"hint": "El pipeline de planificacion explora el codigo, redacta el plan multifase para su aprobacion y luego confirma el documento de seguimiento en el repositorio."
|
|
5302
5304
|
},
|
|
5305
|
+
"planReview": {
|
|
5306
|
+
"title": "Este plan te está esperando",
|
|
5307
|
+
"body": "El planificador redactó las fases y los elementos de abajo. Apruébalos para confirmar el plan y empezar el trabajo, o devuelve el plan indicando qué cambiar.",
|
|
5308
|
+
"approve": "Aprobar plan",
|
|
5309
|
+
"requestChanges": "Solicitar cambios",
|
|
5310
|
+
"feedbackPlaceholder": "¿Qué debería cambiar el planificador? Alcance, orden de las fases, trabajo que falta, un elemento que va en otro sitio…",
|
|
5311
|
+
"sendBack": "Devolver al planificador"
|
|
5312
|
+
},
|
|
5303
5313
|
"planning": {
|
|
5304
5314
|
"title": "Planificar la iniciativa",
|
|
5305
5315
|
"subtitle": "Responde las preguntas del planificador para acotar la iniciativa",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -5294,12 +5294,22 @@
|
|
|
5294
5294
|
"inspector": {
|
|
5295
5295
|
"runPlanning": "Lancer la planification",
|
|
5296
5296
|
"answerPlanning": "Repondre aux questions de planification",
|
|
5297
|
+
"reviewPlan": "Examiner le plan",
|
|
5298
|
+
"resolveDecision": "Résoudre",
|
|
5297
5299
|
"planningInProgress": "Planification en cours",
|
|
5298
5300
|
"pause": "Mettre en pause",
|
|
5299
5301
|
"resume": "Reprendre",
|
|
5300
5302
|
"cancel": "Annuler l'initiative",
|
|
5301
5303
|
"hint": "Le pipeline de planification explore le code, redige le plan multiphase pour approbation, puis valide le document de suivi dans le depot."
|
|
5302
5304
|
},
|
|
5305
|
+
"planReview": {
|
|
5306
|
+
"title": "Ce plan vous attend",
|
|
5307
|
+
"body": "Le planificateur a rédigé les phases et les éléments ci-dessous. Approuvez-les pour valider le plan et lancer le travail, ou renvoyez le plan en indiquant ce qu'il faut changer.",
|
|
5308
|
+
"approve": "Approuver le plan",
|
|
5309
|
+
"requestChanges": "Demander des modifications",
|
|
5310
|
+
"feedbackPlaceholder": "Que doit changer le planificateur ? Périmètre, ordre des phases, travail manquant, un élément qui a sa place ailleurs…",
|
|
5311
|
+
"sendBack": "Renvoyer au planificateur"
|
|
5312
|
+
},
|
|
5303
5313
|
"planning": {
|
|
5304
5314
|
"title": "Planifier l'initiative",
|
|
5305
5315
|
"subtitle": "Repondez aux questions du planificateur pour cadrer l'initiative",
|
package/i18n/locales/he.json
CHANGED
|
@@ -5305,12 +5305,22 @@
|
|
|
5305
5305
|
"inspector": {
|
|
5306
5306
|
"runPlanning": "הרצת תכנון",
|
|
5307
5307
|
"answerPlanning": "מענה על שאלות התכנון",
|
|
5308
|
+
"reviewPlan": "בדיקת התוכנית",
|
|
5309
|
+
"resolveDecision": "פתור",
|
|
5308
5310
|
"planningInProgress": "התכנון מתבצע",
|
|
5309
5311
|
"pause": "השהה",
|
|
5310
5312
|
"resume": "המשך",
|
|
5311
5313
|
"cancel": "ביטול היוזמה",
|
|
5312
5314
|
"hint": "צינור התכנון חוקר את הקוד, מנסח את התוכנית הרב-שלבית לאישור, ואז שומר את מסמך המעקב במאגר."
|
|
5313
5315
|
},
|
|
5316
|
+
"planReview": {
|
|
5317
|
+
"title": "התוכנית הזו ממתינה לך",
|
|
5318
|
+
"body": "המתכנן ניסח את השלבים והפריטים שלמטה. אשרו אותם כדי לשמור את התוכנית ולהתחיל בעבודה, או החזירו את התוכנית עם מה שצריך לשנות.",
|
|
5319
|
+
"approve": "אישור התוכנית",
|
|
5320
|
+
"requestChanges": "בקשת שינויים",
|
|
5321
|
+
"feedbackPlaceholder": "מה המתכנן צריך לשנות? היקף, סדר השלבים, עבודה חסרה, פריט ששייך למקום אחר…",
|
|
5322
|
+
"sendBack": "החזרה למתכנן"
|
|
5323
|
+
},
|
|
5314
5324
|
"planning": {
|
|
5315
5325
|
"title": "תכנון היוזמה",
|
|
5316
5326
|
"subtitle": "ענה על שאלות המתכנן כדי למקד את היוזמה",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4282,12 +4282,22 @@
|
|
|
4282
4282
|
"inspector": {
|
|
4283
4283
|
"runPlanning": "Esegui pianificazione",
|
|
4284
4284
|
"answerPlanning": "Rispondi alle domande di pianificazione",
|
|
4285
|
+
"reviewPlan": "Rivedi il piano",
|
|
4286
|
+
"resolveDecision": "Risolvi",
|
|
4285
4287
|
"planningInProgress": "Pianificazione in corso",
|
|
4286
4288
|
"pause": "Pausa",
|
|
4287
4289
|
"resume": "Riprendi",
|
|
4288
4290
|
"cancel": "Annulla iniziativa",
|
|
4289
4291
|
"hint": "La pipeline di pianificazione esplora il codebase, redige il piano multi-fase per l'approvazione, poi effettua il commit del documento del tracker nel repository."
|
|
4290
4292
|
},
|
|
4293
|
+
"planReview": {
|
|
4294
|
+
"title": "Questo piano ti sta aspettando",
|
|
4295
|
+
"body": "Il planner ha redatto le fasi e gli elementi qui sotto. Approvali per confermare il piano e avviare il lavoro, oppure rimanda indietro il piano indicando cosa cambiare.",
|
|
4296
|
+
"approve": "Approva il piano",
|
|
4297
|
+
"requestChanges": "Richiedi modifiche",
|
|
4298
|
+
"feedbackPlaceholder": "Cosa deve cambiare il planner? Ambito, ordine delle fasi, lavoro mancante, un elemento che sta altrove…",
|
|
4299
|
+
"sendBack": "Rimanda al planner"
|
|
4300
|
+
},
|
|
4291
4301
|
"planning": {
|
|
4292
4302
|
"title": "Pianifica l'iniziativa",
|
|
4293
4303
|
"subtitle": "Rispondi alle domande del pianificatore affinche possa definire l'ambito dell'iniziativa",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -5306,12 +5306,22 @@
|
|
|
5306
5306
|
"inspector": {
|
|
5307
5307
|
"runPlanning": "計画を実行",
|
|
5308
5308
|
"answerPlanning": "計画の質問に回答",
|
|
5309
|
+
"reviewPlan": "計画をレビュー",
|
|
5310
|
+
"resolveDecision": "解決",
|
|
5309
5311
|
"planningInProgress": "計画を実行中",
|
|
5310
5312
|
"pause": "一時停止",
|
|
5311
5313
|
"resume": "再開",
|
|
5312
5314
|
"cancel": "イニシアチブをキャンセル",
|
|
5313
5315
|
"hint": "計画パイプラインはコードベースを調査し、承認用の複数フェーズ計画を起草し、その後トラッカー文書をリポジトリにコミットします。"
|
|
5314
5316
|
},
|
|
5317
|
+
"planReview": {
|
|
5318
|
+
"title": "この計画が承認を待っています",
|
|
5319
|
+
"body": "プランナーが以下のフェーズと項目を起草しました。承認すると計画がコミットされ、作業が始まります。変更したい点がある場合は、その内容を添えて計画を差し戻してください。",
|
|
5320
|
+
"approve": "計画を承認",
|
|
5321
|
+
"requestChanges": "変更を依頼",
|
|
5322
|
+
"feedbackPlaceholder": "プランナーに変更してほしい点は何ですか。範囲、フェーズの順序、不足している作業、別の場所に属する項目など…",
|
|
5323
|
+
"sendBack": "プランナーに差し戻す"
|
|
5324
|
+
},
|
|
5315
5325
|
"planning": {
|
|
5316
5326
|
"title": "イニシアチブを計画",
|
|
5317
5327
|
"subtitle": "プランナーの質問に答えてイニシアチブの範囲を定めます",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -5294,12 +5294,22 @@
|
|
|
5294
5294
|
"inspector": {
|
|
5295
5295
|
"runPlanning": "Uruchom planowanie",
|
|
5296
5296
|
"answerPlanning": "Odpowiedz na pytania planowania",
|
|
5297
|
+
"reviewPlan": "Przejrzyj plan",
|
|
5298
|
+
"resolveDecision": "Rozwiąż",
|
|
5297
5299
|
"planningInProgress": "Planowanie w toku",
|
|
5298
5300
|
"pause": "Wstrzymaj",
|
|
5299
5301
|
"resume": "Wznow",
|
|
5300
5302
|
"cancel": "Anuluj inicjatywe",
|
|
5301
5303
|
"hint": "Pipeline planowania bada kod, przygotowuje wielofazowy plan do zatwierdzenia, a nastepnie zapisuje dokument trackera w repozytorium."
|
|
5302
5304
|
},
|
|
5305
|
+
"planReview": {
|
|
5306
|
+
"title": "Ten plan czeka na Ciebie",
|
|
5307
|
+
"body": "Planer przygotował poniższe fazy i elementy. Zatwierdź je, aby zapisać plan i rozpocząć pracę, albo odeślij plan z informacją, co zmienić.",
|
|
5308
|
+
"approve": "Zatwierdź plan",
|
|
5309
|
+
"requestChanges": "Poproś o zmiany",
|
|
5310
|
+
"feedbackPlaceholder": "Co planer ma zmienić? Zakres, kolejność faz, brakująca praca, element, który pasuje gdzie indziej…",
|
|
5311
|
+
"sendBack": "Odeślij do planera"
|
|
5312
|
+
},
|
|
5303
5313
|
"planning": {
|
|
5304
5314
|
"title": "Zaplanuj inicjatywe",
|
|
5305
5315
|
"subtitle": "Odpowiedz na pytania planisty, aby okreslic zakres inicjatywy",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -5306,12 +5306,22 @@
|
|
|
5306
5306
|
"inspector": {
|
|
5307
5307
|
"runPlanning": "Planlamayi calistir",
|
|
5308
5308
|
"answerPlanning": "Planlama sorularini yanitla",
|
|
5309
|
+
"reviewPlan": "Planı incele",
|
|
5310
|
+
"resolveDecision": "Çöz",
|
|
5309
5311
|
"planningInProgress": "Planlama suruyor",
|
|
5310
5312
|
"pause": "Duraklat",
|
|
5311
5313
|
"resume": "Devam et",
|
|
5312
5314
|
"cancel": "Girisimi iptal et",
|
|
5313
5315
|
"hint": "Planlama hatti kod tabanini inceler, onay icin cok asamali plani hazirlar ve ardindan izleyici belgesini depoya kaydeder."
|
|
5314
5316
|
},
|
|
5317
|
+
"planReview": {
|
|
5318
|
+
"title": "Bu plan sizi bekliyor",
|
|
5319
|
+
"body": "Planlayıcı aşağıdaki aşamaları ve maddeleri hazırladı. Planı kaydedip işe başlamak için onaylayın ya da neyin değişmesi gerektiğini yazarak planı geri gönderin.",
|
|
5320
|
+
"approve": "Planı onayla",
|
|
5321
|
+
"requestChanges": "Değişiklik iste",
|
|
5322
|
+
"feedbackPlaceholder": "Planlayıcı neyi değiştirmeli? Kapsam, aşama sırası, eksik iş, başka yere ait bir madde…",
|
|
5323
|
+
"sendBack": "Planlayıcıya geri gönder"
|
|
5324
|
+
},
|
|
5315
5325
|
"planning": {
|
|
5316
5326
|
"title": "Girisimi planla",
|
|
5317
5327
|
"subtitle": "Girisimi kapsamlandirmak icin planlayicinin sorularini yanitlayin",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -5294,12 +5294,22 @@
|
|
|
5294
5294
|
"inspector": {
|
|
5295
5295
|
"runPlanning": "Запустити планування",
|
|
5296
5296
|
"answerPlanning": "Відповісти на питання планування",
|
|
5297
|
+
"reviewPlan": "Переглянути план",
|
|
5298
|
+
"resolveDecision": "Вирішити",
|
|
5297
5299
|
"planningInProgress": "Планування триває",
|
|
5298
5300
|
"pause": "Призупинити",
|
|
5299
5301
|
"resume": "Відновити",
|
|
5300
5302
|
"cancel": "Скасувати ініціативу",
|
|
5301
5303
|
"hint": "Пайплайн планування досліджує кодову базу, готує багатофазний план на затвердження, а потім комітить документ трекера до репозиторію."
|
|
5302
5304
|
},
|
|
5305
|
+
"planReview": {
|
|
5306
|
+
"title": "Цей план чекає на вас",
|
|
5307
|
+
"body": "Планувальник підготував фази та елементи нижче. Затвердіть їх, щоб зафіксувати план і розпочати роботу, або поверніть план із зазначенням, що змінити.",
|
|
5308
|
+
"approve": "Затвердити план",
|
|
5309
|
+
"requestChanges": "Запросити зміни",
|
|
5310
|
+
"feedbackPlaceholder": "Що має змінити планувальник? Обсяг, порядок фаз, пропущена робота, елемент, якому місце деінде…",
|
|
5311
|
+
"sendBack": "Повернути планувальнику"
|
|
5312
|
+
},
|
|
5303
5313
|
"planning": {
|
|
5304
5314
|
"title": "Спланувати ініціативу",
|
|
5305
5315
|
"subtitle": "Дайте відповіді на питання планувальника, щоб окреслити ініціативу",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.182.
|
|
3
|
+
"version": "0.182.1",
|
|
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",
|