@cat-factory/app 0.181.0 → 0.182.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 +4 -1
- package/app/components/board/nodes/DraggableTask.vue +3 -2
- package/app/components/board/nodes/TaskPipelineMini.vue +9 -12
- package/app/components/docs/DocInterviewWindow.vue +24 -3
- package/app/components/initiative/InitiativePlanningWindow.vue +27 -7
- package/app/composables/useInitiativePlanning.ts +26 -8
- package/app/composables/useTaskExpansion.ts +30 -16
- package/app/modular/panels/inspector.logic.ts +1 -1
- package/app/stores/taskExpansion.spec.ts +88 -0
- package/app/stores/taskExpansion.ts +34 -14
- package/app/utils/catalog.ts +5 -4
- package/app/utils/interviewGate.spec.ts +87 -12
- package/app/utils/interviewGate.ts +57 -5
- package/i18n/locales/de.json +5 -1
- package/i18n/locales/en.json +5 -1
- package/i18n/locales/es.json +5 -1
- package/i18n/locales/fr.json +5 -1
- package/i18n/locales/he.json +5 -1
- package/i18n/locales/it.json +5 -1
- package/i18n/locales/ja.json +5 -1
- package/i18n/locales/pl.json +5 -1
- package/i18n/locales/tr.json +5 -1
- package/i18n/locales/uk.json +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,7 +131,10 @@ example ships in [`deploy/frontend`](../../deploy/frontend) (the `acme:security`
|
|
|
131
131
|
|
|
132
132
|
- **Board canvas** (`components/board`) — `BoardCanvas` + `nodes/` (`BlockNode`,
|
|
133
133
|
`ModuleFrame`, `TaskCard`), dependency edges, the per-block `AgentFailureCard` /
|
|
134
|
-
`AgentStopButton`, and a deep-zoom `focus/BlockFocusView`.
|
|
134
|
+
`AgentStopButton`, and a deep-zoom `focus/BlockFocusView`. A running task card expands
|
|
135
|
+
its build pipeline (`TaskPipelineMini`) on hover at any zoom level, and across every
|
|
136
|
+
on-screen card past the `steps` zoom band — the two grants are combined in the
|
|
137
|
+
`taskExpansion` store and driven by `useTaskExpansion`.
|
|
135
138
|
- **Sidebar & chrome** (`components/layout`) — board/account switchers, palettes
|
|
136
139
|
entry points, the language + [interface-mode](#interface-modes-basic--advanced)
|
|
137
140
|
switchers, the `SpendWarningBanner`, and the toolbar (zoom, LOD, decision queue).
|
|
@@ -10,8 +10,9 @@ const task = computed(() => board.getBlock(props.taskId))
|
|
|
10
10
|
const { draggingId, startDrag } = useBlockDrag()
|
|
11
11
|
|
|
12
12
|
// An expanded pipeline grows downward over its neighbours, so it must stack above the
|
|
13
|
-
// other (compact) task cards — never let a neighbour render on top of the pipeline.
|
|
14
|
-
|
|
13
|
+
// other (compact) task cards — never let a neighbour render on top of the pipeline. Reads
|
|
14
|
+
// the same predicate the pipeline itself renders on, so the two can't disagree.
|
|
15
|
+
const expanded = computed(() => expansion.isExpanded(props.taskId))
|
|
15
16
|
|
|
16
17
|
// Once a task is merged it stops being a unit of work and becomes part of the
|
|
17
18
|
// architecture: it no longer renders as a draggable card (arrows fall back to its
|
|
@@ -12,11 +12,11 @@ import { lodAtLeast } from '~/composables/useSemanticZoom'
|
|
|
12
12
|
import { prReviewPhase } from '~/utils/prReviewProgress'
|
|
13
13
|
import PrReviewPhaseBadge from '~/components/prReview/PrReviewPhaseBadge.vue'
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// todo breakdown expands — done / in-progress / pending — exactly the way
|
|
18
|
-
// zoomed-in bootstrap card reads. Renders nothing until the task has a run and
|
|
19
|
-
//
|
|
15
|
+
// Drill-down inside a task card: the task's build-pipeline steps appear on hover (at any
|
|
16
|
+
// zoom level) or once the `steps` zoom band is reached, and one band deeper (`subtasks`)
|
|
17
|
+
// each step's live todo breakdown expands — done / in-progress / pending — exactly the way
|
|
18
|
+
// a zoomed-in bootstrap card reads. Renders nothing until the task has a run and the card
|
|
19
|
+
// is expanded, so it's safe to mount on every task card.
|
|
20
20
|
const props = defineProps<{ taskId: string }>()
|
|
21
21
|
|
|
22
22
|
const execution = useExecutionStore()
|
|
@@ -40,13 +40,10 @@ const runFailed = computed(() => instance.value?.status === 'failed')
|
|
|
40
40
|
// (spinning "Running") rather than a frozen subtask list.
|
|
41
41
|
const companionByStep = computed(() => steps.value.map((s) => gateCompanionFor(s, runFailed.value)))
|
|
42
42
|
|
|
43
|
-
// Expand the pipeline list
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
const showSteps = computed(
|
|
47
|
-
() =>
|
|
48
|
-
lodAtLeast(lod.value, 'steps') && steps.value.length > 0 && expansion.canExpand(props.taskId),
|
|
49
|
-
)
|
|
43
|
+
// Expand the pipeline list when the board driver says so: this card is hovered (at any
|
|
44
|
+
// zoom level), or the deep zoom bands granted it — on-screen, and the centre-most of any
|
|
45
|
+
// cards that would otherwise overlap. See stores/taskExpansion.ts.
|
|
46
|
+
const showSteps = computed(() => steps.value.length > 0 && expansion.isExpanded(props.taskId))
|
|
50
47
|
const showItems = computed(() => lodAtLeast(lod.value, 'subtasks'))
|
|
51
48
|
|
|
52
49
|
// Clicking a step opens the full agent step-detail overlay — execution metadata
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
import { computed, reactive, watch } from 'vue'
|
|
20
20
|
import InterviewGateNotice from '~/components/common/InterviewGateNotice.vue'
|
|
21
21
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
DOC_INTERVIEWER_KIND,
|
|
24
|
+
interviewGatePhase,
|
|
25
|
+
interviewStepReached,
|
|
26
|
+
} from '~/utils/interviewGate'
|
|
23
27
|
|
|
24
28
|
const board = useBoardStore()
|
|
25
29
|
const docInterview = useDocInterviewStore()
|
|
@@ -63,7 +67,13 @@ const resuming = computed(() => docInterview.resuming)
|
|
|
63
67
|
* actually says, so the questions come back rather than the window sticking on a spinner.
|
|
64
68
|
*/
|
|
65
69
|
const phase = computed(() =>
|
|
66
|
-
resuming.value
|
|
70
|
+
resuming.value
|
|
71
|
+
? 'working'
|
|
72
|
+
: interviewGatePhase(
|
|
73
|
+
session.value?.status,
|
|
74
|
+
run.value?.status,
|
|
75
|
+
interviewStepReached(run.value, DOC_INTERVIEWER_KIND),
|
|
76
|
+
),
|
|
67
77
|
)
|
|
68
78
|
/** The interview converged: the synthesized authoring brief is what the window shows. */
|
|
69
79
|
const converged = computed(() => phase.value === 'converged')
|
|
@@ -133,10 +143,21 @@ const onProceed = () => flushThen((id) => docInterview.proceedInterview(id))
|
|
|
133
143
|
{{ t('docInterview.intro') }}
|
|
134
144
|
</p>
|
|
135
145
|
|
|
146
|
+
<!-- The run is still ahead of the interview (the researcher + outliner steps). Same
|
|
147
|
+
chrome, different claim: nothing has been asked yet, so the "working on your answers"
|
|
148
|
+
copy below would describe answers that do not exist. -->
|
|
149
|
+
<InterviewGateNotice
|
|
150
|
+
v-if="phase === 'preparing'"
|
|
151
|
+
variant="working"
|
|
152
|
+
:title="t('docInterview.preparing')"
|
|
153
|
+
:hint="t('docInterview.preparingHint')"
|
|
154
|
+
testid="doc-interview-preparing"
|
|
155
|
+
/>
|
|
156
|
+
|
|
136
157
|
<!-- A pass is running: the human is waiting on the interviewer. Without this the window is
|
|
137
158
|
byte-identical to the parked state and the submit reads as a no-op. -->
|
|
138
159
|
<InterviewGateNotice
|
|
139
|
-
v-if="phase === 'working'"
|
|
160
|
+
v-else-if="phase === 'working'"
|
|
140
161
|
variant="working"
|
|
141
162
|
:title="t('docInterview.working')"
|
|
142
163
|
:hint="t('docInterview.workingHint')"
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
// (pending `qa` entries with an empty answer) are shown here; the human answers them, then either
|
|
5
5
|
// SUBMITS them (the `continue` action: the interviewer re-runs and may ask follow-ups) or plans
|
|
6
6
|
// now (the `proceed` action: skip the remaining questions — the interviewer converges and the run
|
|
7
|
-
// advances to the analyst
|
|
8
|
-
// because the latter pair both read as "go forward"
|
|
7
|
+
// advances to the planner; the analyst already ran, ahead of this gate). The labels say
|
|
8
|
+
// submit/plan-now rather than continue/proceed because the latter pair both read as "go forward"
|
|
9
|
+
// and were indistinguishable in use.
|
|
9
10
|
// Opened via the universal result-view host: from the inspector / card
|
|
10
11
|
// (`ui.openInitiativePlanning`) or as the interviewer step's result view. Live `initiative`
|
|
11
12
|
// stream events patch the store, so an open window follows the interview as it progresses.
|
|
@@ -29,7 +30,11 @@ import {
|
|
|
29
30
|
isPendingQuestion,
|
|
30
31
|
orderInterviewQuestions,
|
|
31
32
|
} from '~/utils/initiative'
|
|
32
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
INITIATIVE_INTERVIEWER_KIND,
|
|
35
|
+
interviewGatePhase,
|
|
36
|
+
interviewStepReached,
|
|
37
|
+
} from '~/utils/interviewGate'
|
|
33
38
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
34
39
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
35
40
|
|
|
@@ -117,7 +122,11 @@ const resuming = computed(() => initiatives.resuming)
|
|
|
117
122
|
const phase = computed(() =>
|
|
118
123
|
resuming.value
|
|
119
124
|
? 'working'
|
|
120
|
-
: interviewGatePhase(
|
|
125
|
+
: interviewGatePhase(
|
|
126
|
+
initiative.value?.interview?.status,
|
|
127
|
+
run.value?.status,
|
|
128
|
+
interviewStepReached(run.value, INITIATIVE_INTERVIEWER_KIND),
|
|
129
|
+
),
|
|
121
130
|
)
|
|
122
131
|
|
|
123
132
|
/**
|
|
@@ -229,10 +238,21 @@ async function onDiscard() {
|
|
|
229
238
|
{{ t('initiative.planning.intro') }}
|
|
230
239
|
</p>
|
|
231
240
|
|
|
241
|
+
<!-- The run is still ahead of the interview — the codebase analysis that grounds it. It
|
|
242
|
+
wears the working chrome but says something different on purpose: nothing has been
|
|
243
|
+
asked yet, so "working on your answers" would describe answers that do not exist. -->
|
|
244
|
+
<InterviewGateNotice
|
|
245
|
+
v-if="phase === 'preparing'"
|
|
246
|
+
variant="working"
|
|
247
|
+
:title="t('initiative.planning.preparing')"
|
|
248
|
+
:hint="t('initiative.planning.preparingHint')"
|
|
249
|
+
testid="initiative-planning-preparing"
|
|
250
|
+
/>
|
|
251
|
+
|
|
232
252
|
<!-- A pass is running: the human is waiting on the planner. Without this the window is
|
|
233
253
|
byte-identical to the parked state and the submit reads as a no-op. -->
|
|
234
254
|
<InterviewGateNotice
|
|
235
|
-
v-if="phase === 'working'"
|
|
255
|
+
v-else-if="phase === 'working'"
|
|
236
256
|
variant="working"
|
|
237
257
|
:title="t('initiative.planning.working')"
|
|
238
258
|
:hint="t('initiative.planning.workingHint')"
|
|
@@ -316,8 +336,8 @@ async function onDiscard() {
|
|
|
316
336
|
<!-- Action rail. The submit/plan-now pair shows only while the run is actually parked on the
|
|
317
337
|
human: mid-pass they would re-submit a question set already in flight, and the resume is a
|
|
318
338
|
no-op once it isn't. Discard is the opposite — it is offered for as long as a run owns the
|
|
319
|
-
block, because the phases where those two are hidden (working, failed) are
|
|
320
|
-
a wedged run sits in. -->
|
|
339
|
+
block, because the phases where those two are hidden (preparing, working, failed) are
|
|
340
|
+
exactly the ones a wedged run sits in. -->
|
|
321
341
|
<footer
|
|
322
342
|
v-if="initiative && (canDiscard || (phase === 'awaiting' && questions.length > 0))"
|
|
323
343
|
class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
|
|
@@ -4,7 +4,11 @@ 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 {
|
|
7
|
+
import {
|
|
8
|
+
INITIATIVE_INTERVIEWER_KIND,
|
|
9
|
+
interviewGatePhase,
|
|
10
|
+
interviewStepReached,
|
|
11
|
+
} from '~/utils/interviewGate'
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Shared planning affordances for an `initiative`-level block, used by BOTH the board card
|
|
@@ -41,12 +45,14 @@ export function useInitiativePlanning(blockId: MaybeRefOrGetter<string>) {
|
|
|
41
45
|
* The live interview phase, derived from the entity AND the planning run (see
|
|
42
46
|
* {@link interviewGatePhase} for why the run status is load-bearing).
|
|
43
47
|
*/
|
|
44
|
-
const interviewPhase = computed(() =>
|
|
45
|
-
|
|
48
|
+
const interviewPhase = computed(() => {
|
|
49
|
+
const run = execution.getByBlock(toValue(blockId))
|
|
50
|
+
return interviewGatePhase(
|
|
46
51
|
initiative.value?.interview?.status,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
run?.status,
|
|
53
|
+
interviewStepReached(run, INITIATIVE_INTERVIEWER_KIND),
|
|
54
|
+
)
|
|
55
|
+
})
|
|
50
56
|
|
|
51
57
|
/**
|
|
52
58
|
* The interviewer has PARKED the planning run for the human. NOT keyed on whether individual
|
|
@@ -62,8 +68,20 @@ export function useInitiativePlanning(blockId: MaybeRefOrGetter<string>) {
|
|
|
62
68
|
*/
|
|
63
69
|
const awaitingAnswers = computed(() => interviewPhase.value === 'awaiting')
|
|
64
70
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The planning run is mid-flight with nothing for the human to answer — either an interviewer
|
|
73
|
+
* pass is running (`working`) or the run has not reached the interview yet (`preparing`, the
|
|
74
|
+
* codebase analysis that now leads). BOTH phases, deliberately: this drives the card's and
|
|
75
|
+
* inspector's "Planning in progress" button, which is the only route into the planning window
|
|
76
|
+
* while a run owns the block. Narrowing it to `working` would drop that route for the whole of
|
|
77
|
+
* the analysis and fall through to a "Run planning" button for a run already running.
|
|
78
|
+
*
|
|
79
|
+
* The two phases are distinguished INSIDE the window, where there is room to say which is which;
|
|
80
|
+
* the affordance is the same either way.
|
|
81
|
+
*/
|
|
82
|
+
const interviewing = computed(
|
|
83
|
+
() => interviewPhase.value === 'working' || interviewPhase.value === 'preparing',
|
|
84
|
+
)
|
|
67
85
|
|
|
68
86
|
/**
|
|
69
87
|
* Optimistic start flag: flip true the instant "Run planning" is clicked, before the stream
|
|
@@ -15,22 +15,24 @@ function sameSet(a: Set<string>, b: Set<string>) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Board-level driver deciding which task cards
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Board-level driver deciding which task cards expand their full pipeline list.
|
|
19
|
+
* Recomputed every frame against live DOM rects so it follows pan / zoom / drag / resize,
|
|
20
|
+
* and writes two independent grants into the `taskExpansion` store (which combines them —
|
|
21
|
+
* see the store for how they resolve):
|
|
21
22
|
*
|
|
22
|
-
* -
|
|
23
|
-
*
|
|
24
|
-
* task expands only if its footprint doesn't collide with one already granted, so the
|
|
25
|
-
* card you're looking at wins an overlap and the rest stay compact.
|
|
26
|
-
* - hover: the task directly under the pointer is granted first, so hovering any card
|
|
27
|
-
* expands its pipeline regardless of its position on screen. "Under the pointer" is
|
|
28
|
-
* the TOPMOST card at the cursor (document.elementFromPoint), so hovering a region
|
|
23
|
+
* - hover: the task directly under the pointer, at ANY zoom level. "Under the pointer"
|
|
24
|
+
* is the TOPMOST card at the cursor (document.elementFromPoint), so hovering a region
|
|
29
25
|
* already covered by another open pipeline keeps that pipeline, not the card beneath.
|
|
26
|
+
* - zoom: at the deep `steps`/`subtasks` bands, every on-screen card, minus overlaps —
|
|
27
|
+
* two sub-gates:
|
|
28
|
+
* - visibility: a task expands only while its card overlaps the board viewport.
|
|
29
|
+
* - overlap: walking the visible candidates nearest-header-to-screen-centre first, a
|
|
30
|
+
* task expands only if its footprint doesn't collide with one already granted, so
|
|
31
|
+
* the card you're looking at wins an overlap and the rest stay compact. The hovered
|
|
32
|
+
* card is granted first, so it wins every overlap it's part of.
|
|
30
33
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* wouldn't expand never blocks a neighbour.
|
|
34
|
+
* Only tasks with a running pipeline (steps to show) are candidates for either grant — a
|
|
35
|
+
* task that wouldn't expand never blocks a neighbour and never lifts an empty card.
|
|
34
36
|
*/
|
|
35
37
|
export function useTaskExpansion(container: Ref<HTMLElement | null>) {
|
|
36
38
|
const board = useBoardStore()
|
|
@@ -65,14 +67,27 @@ export function useTaskExpansion(container: Ref<HTMLElement | null>) {
|
|
|
65
67
|
// The task whose card is topmost at the pointer, or null. Using elementFromPoint (not a
|
|
66
68
|
// rect test) means an open pipeline stacked above a neighbour wins the hit, so hovering
|
|
67
69
|
// a region obscured by another pipeline doesn't switch to the card hidden beneath it.
|
|
70
|
+
//
|
|
71
|
+
// Blocks with no pipeline to show are filtered out here rather than left to the card:
|
|
72
|
+
// a frame, a module, or a task with no run expands to nothing, and granting it would
|
|
73
|
+
// still lift an empty card over its neighbours (see DraggableTask's z-index).
|
|
68
74
|
function hoveredTaskId(): string | null {
|
|
69
75
|
if (!pointer) return null
|
|
70
76
|
const hit = document.elementFromPoint(pointer.x, pointer.y)
|
|
71
|
-
|
|
77
|
+
const id = hit?.closest('[data-block-id]')?.getAttribute('data-block-id') ?? null
|
|
78
|
+
if (!id || !execution.getByBlock(id)?.steps.length) return null
|
|
79
|
+
return id
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
function recompute() {
|
|
75
|
-
//
|
|
83
|
+
// Hover expands a card at ANY zoom band, so the pointer hit is resolved BEFORE the
|
|
84
|
+
// zoom gate below — resolving it after would collapse the hovered card the moment the
|
|
85
|
+
// user zoomed back out past the `steps` band.
|
|
86
|
+
const hovered = hoveredTaskId()
|
|
87
|
+
if (store.hoveredId !== hovered) store.setHovered(hovered)
|
|
88
|
+
|
|
89
|
+
// The zoom-driven expansion (every on-screen card, overlap-resolved) is deep-band
|
|
90
|
+
// only; clear its grants otherwise. The hover grant above stands on its own.
|
|
76
91
|
if (!lodAtLeast(ui.lod, 'steps')) {
|
|
77
92
|
if (store.allowed.size) store.setAllowed(new Set())
|
|
78
93
|
return
|
|
@@ -117,7 +132,6 @@ export function useTaskExpansion(container: Ref<HTMLElement | null>) {
|
|
|
117
132
|
// clears every footprint already granted, so the centre-most card wins any overlap.
|
|
118
133
|
// The hovered card is granted FIRST, so hovering a card expands it regardless of its
|
|
119
134
|
// distance from the centre (and a centre-most neighbour it overlaps yields to it).
|
|
120
|
-
const hovered = hoveredTaskId()
|
|
121
135
|
const claimed: Rect[] = []
|
|
122
136
|
const next = new Set<string>()
|
|
123
137
|
const hoveredCard = hovered ? candidates.find((c) => c.id === hovered) : undefined
|
|
@@ -76,7 +76,7 @@ const isTask = (b: Block) => b.level === 'task'
|
|
|
76
76
|
const isFrame = (b: Block) => b.level === 'frame'
|
|
77
77
|
/**
|
|
78
78
|
* A block whose inspector carries a pipeline RUN. An initiative's planning pipeline is an
|
|
79
|
-
* ordinary run of ordinary agent steps (
|
|
79
|
+
* ordinary run of ordinary agent steps (analyst → interviewer → planner → committer), so it
|
|
80
80
|
* gets the same execution panel a task does — step list, live phases, step-detail drill-down,
|
|
81
81
|
* and the Stop / Discard-run controls that are the only way to unwedge a stalled planning run.
|
|
82
82
|
* Before this it had no run surface at all, which is why a stuck plan was a dead end.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { useTaskExpansionStore } from '~/stores/taskExpansion'
|
|
3
|
+
import { useUiStore } from '~/stores/ui'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The expansion gate combines two independent grants (hover at any zoom, the deep zoom
|
|
7
|
+
* bands otherwise). Both `TaskPipelineMini` (what renders) and `DraggableTask` (what
|
|
8
|
+
* stacks on top) read `isExpanded`, so these cases pin the rule they share.
|
|
9
|
+
*
|
|
10
|
+
* Zoom is set through the ui store's raw `zoom`, the same value the board canvas writes;
|
|
11
|
+
* `lod` derives from it (< 1.8 is shallower than the `steps` band).
|
|
12
|
+
*/
|
|
13
|
+
function setup(zoom: number) {
|
|
14
|
+
const ui = useUiStore()
|
|
15
|
+
ui.zoom = zoom
|
|
16
|
+
const store = useTaskExpansionStore()
|
|
17
|
+
store.setDriverActive(true)
|
|
18
|
+
return store
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('taskExpansion — hover expands at any zoom level', () => {
|
|
22
|
+
it('expands the hovered card while zoomed out past the steps band', () => {
|
|
23
|
+
const store = setup(0.5) // `far`
|
|
24
|
+
store.setHovered('task-a')
|
|
25
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
26
|
+
// Hover is a grant of ONE card: its neighbours stay compact at this zoom.
|
|
27
|
+
expect(store.isExpanded('task-b')).toBe(false)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('expands the hovered card at every band, not just the shallow ones', () => {
|
|
31
|
+
const store = setup(1) // `mid`
|
|
32
|
+
store.setHovered('task-a')
|
|
33
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
34
|
+
useUiStore().zoom = 1.5 // `close`
|
|
35
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
36
|
+
useUiStore().zoom = 3 // `subtasks`
|
|
37
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('collapses again once the pointer leaves the card', () => {
|
|
41
|
+
const store = setup(0.5)
|
|
42
|
+
store.setHovered('task-a')
|
|
43
|
+
store.setHovered(null)
|
|
44
|
+
expect(store.isExpanded('task-a')).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('expands a hovered card the zoom gate denied for overlapping a neighbour', () => {
|
|
48
|
+
const store = setup(2) // `steps`
|
|
49
|
+
store.setAllowed(new Set(['task-b']))
|
|
50
|
+
store.setHovered('task-a')
|
|
51
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
52
|
+
expect(store.isExpanded('task-b')).toBe(true)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('taskExpansion — the zoom grant is unchanged', () => {
|
|
57
|
+
it('honours the driver grant only from the steps band up', () => {
|
|
58
|
+
const store = setup(1.5) // `close` — one band shallower than `steps`
|
|
59
|
+
store.setAllowed(new Set(['task-a']))
|
|
60
|
+
expect(store.isExpanded('task-a')).toBe(false)
|
|
61
|
+
useUiStore().zoom = 2 // `steps`
|
|
62
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('denies a card the driver left out of the permitted set', () => {
|
|
66
|
+
const store = setup(2)
|
|
67
|
+
store.setAllowed(new Set(['task-b']))
|
|
68
|
+
expect(store.isExpanded('task-a')).toBe(false)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('falls back to allowed with no driver mounted, still only at the deep bands', () => {
|
|
72
|
+
const ui = useUiStore()
|
|
73
|
+
ui.zoom = 2
|
|
74
|
+
const store = useTaskExpansionStore() // driverActive stays false
|
|
75
|
+
expect(store.isExpanded('task-a')).toBe(true)
|
|
76
|
+
ui.zoom = 1
|
|
77
|
+
expect(store.isExpanded('task-a')).toBe(false)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('drops both grants when the driver unmounts', () => {
|
|
81
|
+
const store = setup(2)
|
|
82
|
+
store.setAllowed(new Set(['task-a']))
|
|
83
|
+
store.setHovered('task-b')
|
|
84
|
+
store.setDriverActive(false)
|
|
85
|
+
expect(store.allowed.size).toBe(0)
|
|
86
|
+
expect(store.hoveredId).toBeNull()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -1,37 +1,57 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
2
|
import { ref } from 'vue'
|
|
3
|
+
import { lodAtLeast } from '~/composables/useSemanticZoom'
|
|
4
|
+
import { useUiStore } from '~/stores/ui'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
|
-
* Which task cards
|
|
7
|
+
* Which task cards render their full build-pipeline list.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* recomputes a permitted set every frame — only on-screen cards, the one closest to
|
|
11
|
-
* the screen centre when two would overlap, plus whichever card the pointer is hovering
|
|
12
|
-
* (hover expands a card regardless of position) — and writes it here.
|
|
13
|
-
* `TaskPipelineMini` reads `canExpand` to decide whether to expand or stay compact.
|
|
9
|
+
* Two independent grants, both written every frame by the board driver
|
|
10
|
+
* (`useTaskExpansion`) and combined HERE so the render (`TaskPipelineMini`) and the
|
|
11
|
+
* stacking (`DraggableTask`) can never disagree about which cards are expanded:
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* - HOVER — the card under the pointer expands at ANY zoom level. Pointing at a task
|
|
14
|
+
* is asking what it is doing right now, and that answer used to be reachable only
|
|
15
|
+
* by first zooming past the `steps` band.
|
|
16
|
+
* - ZOOM — past the `steps` band, every on-screen card expands. Deep-zoom grows a card
|
|
17
|
+
* downward and cards are absolutely positioned in their frame, so several expanded
|
|
18
|
+
* cards stacked vertically pile on top of each other; the driver denies whichever
|
|
19
|
+
* would collide with a card nearer the screen centre and writes the survivors here.
|
|
20
|
+
*
|
|
21
|
+
* `driverActive` lets the zoom grant degrade gracefully: with no board driver mounted
|
|
22
|
+
* (e.g. a card rendered in isolation) it falls back to "allowed", so the plain zoom
|
|
23
|
+
* behaviour is unchanged. The hover grant needs no fallback — without the driver
|
|
24
|
+
* nothing is hovered.
|
|
18
25
|
*/
|
|
19
26
|
export const useTaskExpansionStore = defineStore('taskExpansion', () => {
|
|
27
|
+
const ui = useUiStore()
|
|
20
28
|
const allowed = ref<Set<string>>(new Set())
|
|
29
|
+
const hoveredId = ref<string | null>(null)
|
|
21
30
|
const driverActive = ref(false)
|
|
22
31
|
|
|
23
32
|
function setAllowed(ids: Set<string>) {
|
|
24
33
|
allowed.value = ids
|
|
25
34
|
}
|
|
26
35
|
|
|
36
|
+
function setHovered(id: string | null) {
|
|
37
|
+
hoveredId.value = id
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
function setDriverActive(active: boolean) {
|
|
28
41
|
driverActive.value = active
|
|
29
|
-
if (!active)
|
|
42
|
+
if (!active) {
|
|
43
|
+
allowed.value = new Set()
|
|
44
|
+
hoveredId.value = null
|
|
45
|
+
}
|
|
30
46
|
}
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
/** Whether this task card should render its pipeline: hovered at any zoom, else
|
|
49
|
+
* granted by the deep-zoom bands. */
|
|
50
|
+
function isExpanded(id: string) {
|
|
51
|
+
if (hoveredId.value === id) return true
|
|
52
|
+
if (!lodAtLeast(ui.lod, 'steps')) return false
|
|
33
53
|
return driverActive.value ? allowed.value.has(id) : true
|
|
34
54
|
}
|
|
35
55
|
|
|
36
|
-
return { allowed, driverActive, setAllowed, setDriverActive,
|
|
56
|
+
return { allowed, hoveredId, driverActive, setAllowed, setHovered, setDriverActive, isExpanded }
|
|
37
57
|
})
|
package/app/utils/catalog.ts
CHANGED
|
@@ -439,16 +439,17 @@ export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
|
|
|
439
439
|
description:
|
|
440
440
|
'Provisions the ephemeral environment the tester and human-test gate run against (kubernetes / custom services); a no-op for docker-compose / infraless.',
|
|
441
441
|
},
|
|
442
|
-
// The Initiative Planning pipeline's
|
|
442
|
+
// The Initiative Planning pipeline's steps. Only runnable on an initiative
|
|
443
443
|
// block (pl_initiative — enforced by the engine), so they are display-metadata
|
|
444
|
-
// system kinds, never palette archetypes.
|
|
444
|
+
// system kinds, never palette archetypes. The analyst runs FIRST, ahead of the
|
|
445
|
+
// interviewer, so the interview covers only what the code cannot answer.
|
|
445
446
|
'initiative-interviewer': {
|
|
446
447
|
kind: 'initiative-interviewer',
|
|
447
448
|
label: 'Initiative Interviewer',
|
|
448
449
|
icon: 'i-lucide-messages-square',
|
|
449
450
|
color: '#818cf8',
|
|
450
451
|
description:
|
|
451
|
-
'Interviews you on the goals, scope and constraints
|
|
452
|
+
'Interviews you on the goals, scope and constraints the codebase cannot answer, then synthesizes the agreed brief the planner builds on.',
|
|
452
453
|
// Opens the dedicated planning Q&A window (answer / continue / proceed) while parked.
|
|
453
454
|
resultView: 'initiative-planning',
|
|
454
455
|
},
|
|
@@ -458,7 +459,7 @@ export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
|
|
|
458
459
|
icon: 'i-lucide-microscope',
|
|
459
460
|
color: '#818cf8',
|
|
460
461
|
description:
|
|
461
|
-
'Explores the codebase and writes an analysis (architecture, touch points, risks) that grounds the plan. Makes no changes.',
|
|
462
|
+
'Explores the codebase first and writes an analysis (architecture, touch points, risks) that grounds both the interview and the plan. Makes no changes.',
|
|
462
463
|
resultView: 'initiative-tracker',
|
|
463
464
|
},
|
|
464
465
|
'initiative-planner': {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DOC_INTERVIEWER_KIND,
|
|
4
|
+
INITIATIVE_INTERVIEWER_KIND,
|
|
5
|
+
interviewGatePhase,
|
|
6
|
+
interviewStepReached,
|
|
7
|
+
} from './interviewGate'
|
|
8
|
+
import type { ExecutionInstance } from '~/types/domain'
|
|
3
9
|
|
|
4
10
|
// `interviewGatePhase` is what stops continue/proceed reading as no-ops in BOTH interview windows
|
|
5
11
|
// (initiative planning, document interview): the resume is asynchronous — the HTTP call only wakes
|
|
@@ -7,45 +13,114 @@ import { interviewGatePhase } from './interviewGate'
|
|
|
7
13
|
// what distinguishes "parked, waiting on you" from "a pass is running", a distinction the entity
|
|
8
14
|
// alone cannot make.
|
|
9
15
|
|
|
16
|
+
/** The interview step has been reached — the default for every case not about the split below. */
|
|
17
|
+
const REACHED = true
|
|
18
|
+
|
|
10
19
|
describe('interviewGatePhase', () => {
|
|
11
20
|
it('is awaiting while the run is parked on the human', () => {
|
|
12
|
-
expect(interviewGatePhase('awaiting', 'blocked')).toBe('awaiting')
|
|
21
|
+
expect(interviewGatePhase('awaiting', 'blocked', REACHED)).toBe('awaiting')
|
|
13
22
|
})
|
|
14
23
|
|
|
15
24
|
it('is working once the resumed run is running again, even though the entity still says awaiting', () => {
|
|
16
25
|
// The exact regression: continue/proceed leave the entity's status untouched until the pass
|
|
17
26
|
// finishes, so an entity-only reading renders the same questions and looks like a dead button.
|
|
18
|
-
expect(interviewGatePhase('awaiting', 'running')).toBe('working')
|
|
27
|
+
expect(interviewGatePhase('awaiting', 'running', REACHED)).toBe('working')
|
|
19
28
|
})
|
|
20
29
|
|
|
21
30
|
it('is working for the FIRST pass, before any question exists', () => {
|
|
22
|
-
expect(interviewGatePhase(undefined, 'running')).toBe('working')
|
|
31
|
+
expect(interviewGatePhase(undefined, 'running', REACHED)).toBe('working')
|
|
23
32
|
})
|
|
24
33
|
|
|
25
34
|
it('is failed when the run stopped before the interview settled', () => {
|
|
26
35
|
// Must not stay `working`: a pass that dies would otherwise spin forever.
|
|
27
|
-
expect(interviewGatePhase('awaiting', 'failed')).toBe('failed')
|
|
28
|
-
expect(interviewGatePhase(undefined, 'failed')).toBe('failed')
|
|
36
|
+
expect(interviewGatePhase('awaiting', 'failed', REACHED)).toBe('failed')
|
|
37
|
+
expect(interviewGatePhase(undefined, 'failed', REACHED)).toBe('failed')
|
|
29
38
|
})
|
|
30
39
|
|
|
31
40
|
it('is converged once the interview settled, whatever the run went on to do', () => {
|
|
32
41
|
// `converged` outranks `failed`: a later step's failure belongs to that step, not the
|
|
33
42
|
// interview, and the block's own failure surface reports it.
|
|
34
|
-
expect(interviewGatePhase('done', 'running')).toBe('converged')
|
|
35
|
-
expect(interviewGatePhase('done', 'failed')).toBe('converged')
|
|
36
|
-
expect(interviewGatePhase('done', undefined)).toBe('converged')
|
|
43
|
+
expect(interviewGatePhase('done', 'running', REACHED)).toBe('converged')
|
|
44
|
+
expect(interviewGatePhase('done', 'failed', REACHED)).toBe('converged')
|
|
45
|
+
expect(interviewGatePhase('done', undefined, REACHED)).toBe('converged')
|
|
37
46
|
})
|
|
38
47
|
|
|
39
48
|
it('is idle when the interview never ran', () => {
|
|
40
|
-
expect(interviewGatePhase(undefined, undefined)).toBe('idle')
|
|
49
|
+
expect(interviewGatePhase(undefined, undefined, REACHED)).toBe('idle')
|
|
41
50
|
})
|
|
42
51
|
|
|
43
52
|
it('degrades to the entity-only reading when the run is not cached', () => {
|
|
44
53
|
// A window opened before the execution snapshot lands must show the questions, never a spinner.
|
|
45
|
-
expect(interviewGatePhase('awaiting', undefined)).toBe('awaiting')
|
|
54
|
+
expect(interviewGatePhase('awaiting', undefined, REACHED)).toBe('awaiting')
|
|
46
55
|
})
|
|
47
56
|
|
|
48
57
|
it('keeps a paused run answerable', () => {
|
|
49
|
-
expect(interviewGatePhase('awaiting', 'paused')).toBe('awaiting')
|
|
58
|
+
expect(interviewGatePhase('awaiting', 'paused', REACHED)).toBe('awaiting')
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Neither interview leads its pipeline — initiative planning explores the codebase first, the
|
|
63
|
+
// document pipeline researches and outlines first — and that lead-in is minutes of container work.
|
|
64
|
+
// Reported as `working` it claims an interviewer is chewing on answers the human was never asked
|
|
65
|
+
// for; these pin the split.
|
|
66
|
+
describe('interviewGatePhase — before the interview step is reached', () => {
|
|
67
|
+
it('is preparing while an EARLIER step is running', () => {
|
|
68
|
+
expect(interviewGatePhase(undefined, 'running', false)).toBe('preparing')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('does not offer a stale previous round to answer during the lead-in', () => {
|
|
72
|
+
// On a re-plan the entity still carries the last run's questions until the gate's
|
|
73
|
+
// `resetForFreshRun` fires, which is now AFTER the lead-in. Reading those as `awaiting` would
|
|
74
|
+
// invite the human to answer a round about to be discarded.
|
|
75
|
+
expect(interviewGatePhase('awaiting', 'running', false)).toBe('preparing')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('still reports a settled or failed run over the lead-in', () => {
|
|
79
|
+
expect(interviewGatePhase('done', 'running', false)).toBe('converged')
|
|
80
|
+
expect(interviewGatePhase(undefined, 'failed', false)).toBe('failed')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('is unchanged when the run is not running at all', () => {
|
|
84
|
+
expect(interviewGatePhase('awaiting', 'blocked', false)).toBe('awaiting')
|
|
85
|
+
expect(interviewGatePhase(undefined, undefined, false)).toBe('idle')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const run = (kinds: string[], currentStep: number) =>
|
|
90
|
+
({ steps: kinds.map((agentKind) => ({ agentKind })), currentStep }) as Pick<
|
|
91
|
+
ExecutionInstance,
|
|
92
|
+
'steps' | 'currentStep'
|
|
93
|
+
>
|
|
94
|
+
|
|
95
|
+
describe('interviewStepReached', () => {
|
|
96
|
+
const PLANNING = ['initiative-analyst', INITIATIVE_INTERVIEWER_KIND, 'initiative-planner']
|
|
97
|
+
|
|
98
|
+
it('is false while an earlier step is current', () => {
|
|
99
|
+
expect(interviewStepReached(run(PLANNING, 0), INITIATIVE_INTERVIEWER_KIND)).toBe(false)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('is true from the interview step onward', () => {
|
|
103
|
+
expect(interviewStepReached(run(PLANNING, 1), INITIATIVE_INTERVIEWER_KIND)).toBe(true)
|
|
104
|
+
expect(interviewStepReached(run(PLANNING, 2), INITIATIVE_INTERVIEWER_KIND)).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('degrades to true when the run is not cached yet', () => {
|
|
108
|
+
// Over-reporting "still preparing" would leave a genuinely parked interview looking dormant,
|
|
109
|
+
// which is worse than generic copy — so an unknown run keeps the pre-existing reading.
|
|
110
|
+
// Both spellings of "no run": `useResultViewRunMeta` resolves to null, a store lookup to
|
|
111
|
+
// undefined, and the two windows use one each.
|
|
112
|
+
expect(interviewStepReached(undefined, INITIATIVE_INTERVIEWER_KIND)).toBe(true)
|
|
113
|
+
expect(interviewStepReached(null, INITIATIVE_INTERVIEWER_KIND)).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('degrades to true for a chain carrying no such step', () => {
|
|
117
|
+
expect(
|
|
118
|
+
interviewStepReached(run(['doc-researcher', 'doc-writer'], 0), DOC_INTERVIEWER_KIND),
|
|
119
|
+
).toBe(true)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('locates the document interviewer behind its own lead-in', () => {
|
|
123
|
+
const authoring = run(['doc-researcher', 'doc-outliner', DOC_INTERVIEWER_KIND], 1)
|
|
124
|
+
expect(interviewStepReached(authoring, DOC_INTERVIEWER_KIND)).toBe(false)
|
|
50
125
|
})
|
|
51
126
|
})
|
|
@@ -12,15 +12,57 @@ import type { ExecutionInstance } from '~/types/domain'
|
|
|
12
12
|
* disagree about whether there is anything to answer.
|
|
13
13
|
*
|
|
14
14
|
* - `idle` — the interview has not run yet (nothing to answer, nothing in flight).
|
|
15
|
+
* - `preparing` — the run is working on an EARLIER step; the interview has not begun.
|
|
15
16
|
* - `working` — an interviewer pass is running; the human waits.
|
|
16
17
|
* - `awaiting` — the run is parked on the human's answers.
|
|
17
18
|
* - `converged` — the interview settled; the run moved on.
|
|
18
19
|
* - `failed` — the run stopped before the interview settled.
|
|
19
20
|
*/
|
|
20
|
-
export type InterviewGatePhase =
|
|
21
|
+
export type InterviewGatePhase =
|
|
22
|
+
| 'idle'
|
|
23
|
+
| 'preparing'
|
|
24
|
+
| 'working'
|
|
25
|
+
| 'awaiting'
|
|
26
|
+
| 'converged'
|
|
27
|
+
| 'failed'
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
|
-
*
|
|
30
|
+
* The agent kind of each gate's own step, which is what {@link interviewStepReached} locates in the
|
|
31
|
+
* run's chain. Kept here beside the phase they feed rather than inline at each window, so the two
|
|
32
|
+
* surfaces can't drift onto different spellings of the same step.
|
|
33
|
+
*/
|
|
34
|
+
export const INITIATIVE_INTERVIEWER_KIND = 'initiative-interviewer'
|
|
35
|
+
export const DOC_INTERVIEWER_KIND = 'doc-interviewer'
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether the run has REACHED the interview gate's own step — i.e. whether a running run is
|
|
39
|
+
* running the interviewer, or something ahead of it.
|
|
40
|
+
*
|
|
41
|
+
* Neither gate leads its pipeline: `pl_initiative` explores the codebase first, and the document
|
|
42
|
+
* pipeline researches and outlines first. Without this the whole of that lead-in reads as
|
|
43
|
+
* `working`, so the window claims an interviewer pass is chewing on answers the human has not
|
|
44
|
+
* given yet — for however long a container step takes.
|
|
45
|
+
*
|
|
46
|
+
* Degrades to `true` (today's reading, no `preparing` claim) when the run is not cached yet or the
|
|
47
|
+
* chain carries no such step: a phase that over-reports "we are still preparing" would leave a
|
|
48
|
+
* genuinely-parked interview looking dormant, which is worse than the copy being generic.
|
|
49
|
+
*
|
|
50
|
+
* Takes `null` as well as `undefined` because the two window seams spell "no run" differently —
|
|
51
|
+
* `useResultViewRunMeta` resolves to `null`, a store lookup to `undefined` — and both mean the same
|
|
52
|
+
* thing here.
|
|
53
|
+
*/
|
|
54
|
+
export function interviewStepReached(
|
|
55
|
+
run: Pick<ExecutionInstance, 'steps' | 'currentStep'> | null | undefined,
|
|
56
|
+
agentKind: string,
|
|
57
|
+
): boolean {
|
|
58
|
+
if (!run) return true
|
|
59
|
+
const index = run.steps.findIndex((step) => step.agentKind === agentKind)
|
|
60
|
+
return index < 0 || index <= run.currentStep
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the phase from the interview entity's status, its run's status, and whether the run has
|
|
65
|
+
* reached the interview step ({@link interviewStepReached}).
|
|
24
66
|
*
|
|
25
67
|
* The RUN status is load-bearing, not redundant. Continue/proceed are ASYNC by design: the HTTP
|
|
26
68
|
* call only records the intent on the parked step and wakes the durable driver, which then runs
|
|
@@ -35,17 +77,27 @@ export type InterviewGatePhase = 'idle' | 'working' | 'awaiting' | 'converged' |
|
|
|
35
77
|
* says so instead of spinning forever. An unknown run (no instance cached yet) degrades to the
|
|
36
78
|
* entity-only reading, never to a spinner.
|
|
37
79
|
*
|
|
80
|
+
* `stepReached` splits that running window in two, because "a pass is in flight" and "your turn is
|
|
81
|
+
* still coming" are different things to be told and the difference is minutes long. It is a
|
|
82
|
+
* REQUIRED argument rather than an optional one: a caller that omitted it would silently get the
|
|
83
|
+
* misleading half, which is the bug this exists to fix.
|
|
84
|
+
*
|
|
38
85
|
* `converged` wins over `failed` on purpose: once the interview settled, a later failure belongs
|
|
39
|
-
* to the step that failed (the
|
|
40
|
-
*
|
|
86
|
+
* to the step that failed (the planner, the writer), and the block's own failure surface reports
|
|
87
|
+
* it — the interview window claiming the interview broke would be wrong.
|
|
41
88
|
*/
|
|
42
89
|
export function interviewGatePhase(
|
|
43
90
|
status: 'awaiting' | 'done' | undefined,
|
|
44
91
|
runStatus: ExecutionInstance['status'] | undefined,
|
|
92
|
+
stepReached: boolean,
|
|
45
93
|
): InterviewGatePhase {
|
|
46
94
|
if (status === 'done') return 'converged'
|
|
47
95
|
if (runStatus === 'failed') return 'failed'
|
|
48
|
-
|
|
96
|
+
// Before `status === 'awaiting'`: on a re-plan the entity still carries the PREVIOUS run's
|
|
97
|
+
// questions until the gate's `resetForFreshRun` fires, which now happens after the lead-in
|
|
98
|
+
// steps. Reading those as "awaiting your answers" would invite a human to answer a round that
|
|
99
|
+
// is about to be discarded.
|
|
100
|
+
if (runStatus === 'running') return stepReached ? 'working' : 'preparing'
|
|
49
101
|
if (status === 'awaiting') return 'awaiting'
|
|
50
102
|
return 'idle'
|
|
51
103
|
}
|
package/i18n/locales/de.json
CHANGED
|
@@ -4294,9 +4294,11 @@
|
|
|
4294
4294
|
"intro": "Der Planer grenzt diese Initiative ein. Beantworten Sie seine Fragen, um Ziel und Einschränkungen zu formen, und senden Sie sie dann ab. Sie können ihn auch mit dem, was er bereits hat, jetzt planen lassen.",
|
|
4295
4295
|
"empty": "Keine Initiative für diesen Block gefunden.",
|
|
4296
4296
|
"converged": "Keine Fragen ausstehend. Der Planer hat, was er braucht, und entwirft den Plan.",
|
|
4297
|
-
"idle": "Die Planung wurde noch nicht gestartet. Führen Sie die Planung von der Initiative aus,
|
|
4297
|
+
"idle": "Die Planung wurde noch nicht gestartet. Führen Sie die Planung von der Initiative aus: Zuerst wird die Codebasis analysiert, danach werden Sie befragt.",
|
|
4298
4298
|
"working": "Der Planer verarbeitet Ihre Antworten",
|
|
4299
4299
|
"workingHint": "Das dauert einen Moment. Rückfragen erscheinen hier, sobald er fertig ist, oder er beginnt mit dem Entwurf des Plans, wenn ihm die Angaben genügen.",
|
|
4300
|
+
"preparing": "Die Codebasis wird vor dem Interview analysiert",
|
|
4301
|
+
"preparingHint": "Die Planung liest zuerst das Repository, damit Sie nicht nach dem gefragt werden, was der Code beantworten kann. Fragen erscheinen hier, sobald das erledigt ist.",
|
|
4300
4302
|
"failed": "Der Planungslauf wurde abgebrochen",
|
|
4301
4303
|
"failedHint": "Er endete, bevor der Planer antworten konnte. Ihre Antworten sind gespeichert; führen Sie die Planung von der Initiative aus erneut aus.",
|
|
4302
4304
|
"answerPlaceholder": "Ihre Antwort",
|
|
@@ -4647,6 +4649,8 @@
|
|
|
4647
4649
|
"converged": "Es stehen keine Fragen aus. Der Interviewer hat, was er braucht, und das Dokument wird entworfen.",
|
|
4648
4650
|
"working": "Der Interviewer verarbeitet deine Antworten",
|
|
4649
4651
|
"workingHint": "Das dauert einen Moment. Nachfragen erscheinen hier, sobald er fertig ist, oder der Entwurf beginnt, wenn ihm die Angaben genügen.",
|
|
4652
|
+
"preparing": "Das Dokument wird vor dem Interview recherchiert",
|
|
4653
|
+
"preparingHint": "Der Lauf sammelt zuerst Hintergrundwissen und entwirft eine Gliederung. Fragen erscheinen hier, sobald das erledigt ist.",
|
|
4650
4654
|
"failed": "Der Dokumentenlauf wurde abgebrochen",
|
|
4651
4655
|
"failedHint": "Er endete, bevor der Interviewer antworten konnte. Deine Antworten sind gespeichert; führe die Dokumentaufgabe erneut aus.",
|
|
4652
4656
|
"answerPlaceholder": "Deine Antwort",
|
package/i18n/locales/en.json
CHANGED
|
@@ -3678,6 +3678,8 @@
|
|
|
3678
3678
|
"converged": "No questions are pending. The interviewer has what it needs and the document is being drafted.",
|
|
3679
3679
|
"working": "The interviewer is working on your answers",
|
|
3680
3680
|
"workingHint": "This takes a moment. Follow-up questions appear here when it is done, or drafting starts if it has enough.",
|
|
3681
|
+
"preparing": "Researching the document before the interview",
|
|
3682
|
+
"preparingHint": "The run gathers background and drafts an outline first. Questions appear here once that is done.",
|
|
3681
3683
|
"failed": "The document run stopped",
|
|
3682
3684
|
"failedHint": "It ended before the interviewer could answer. Your answers are saved; re-run the document task to try again.",
|
|
3683
3685
|
"answerPlaceholder": "Your answer",
|
|
@@ -5469,9 +5471,11 @@
|
|
|
5469
5471
|
"intro": "The planner is scoping this initiative. Answer its questions to shape the goal and constraints, then submit them. You can also have it plan now with what it already has.",
|
|
5470
5472
|
"empty": "No initiative found for this block.",
|
|
5471
5473
|
"converged": "No questions are pending. The planner has what it needs and is drafting the plan.",
|
|
5472
|
-
"idle": "Planning has not started yet. Run planning from the initiative
|
|
5474
|
+
"idle": "Planning has not started yet. Run planning from the initiative: it analyzes the codebase first, then interviews you.",
|
|
5473
5475
|
"working": "The planner is working on your answers",
|
|
5474
5476
|
"workingHint": "This takes a moment. Follow-up questions appear here when it is done, or it starts drafting the plan if it has enough.",
|
|
5477
|
+
"preparing": "Exploring the codebase before the interview",
|
|
5478
|
+
"preparingHint": "Planning reads the repository first, so you are not asked what the code can answer. Questions appear here once that is done.",
|
|
5475
5479
|
"failed": "The planning run stopped",
|
|
5476
5480
|
"failedHint": "It ended before the planner could answer. Your answers are saved; re-run planning from the initiative to try again.",
|
|
5477
5481
|
"answerPlaceholder": "Your answer",
|
package/i18n/locales/es.json
CHANGED
|
@@ -3570,6 +3570,8 @@
|
|
|
3570
3570
|
"converged": "No hay preguntas pendientes. El entrevistador tiene lo que necesita y el documento se está redactando.",
|
|
3571
3571
|
"working": "El entrevistador está procesando tus respuestas",
|
|
3572
3572
|
"workingHint": "Esto tarda un momento. Las preguntas de seguimiento aparecerán aquí cuando termine, o empezará la redacción si ya tiene suficiente.",
|
|
3573
|
+
"preparing": "Investigando el documento antes de la entrevista",
|
|
3574
|
+
"preparingHint": "La ejecución primero reúne el contexto y esboza un esquema. Las preguntas aparecerán aquí cuando termine.",
|
|
3573
3575
|
"failed": "La ejecución del documento se detuvo",
|
|
3574
3576
|
"failedHint": "Terminó antes de que el entrevistador pudiera responder. Tus respuestas están guardadas; vuelve a ejecutar la tarea del documento.",
|
|
3575
3577
|
"answerPlaceholder": "Tu respuesta",
|
|
@@ -5304,9 +5306,11 @@
|
|
|
5304
5306
|
"intro": "El planificador esta acotando esta iniciativa. Responde sus preguntas para definir el objetivo y las restricciones, y luego envialas. Tambien puedes pedirle que planifique ahora con lo que ya tiene.",
|
|
5305
5307
|
"empty": "No se encontro ninguna iniciativa para este bloque.",
|
|
5306
5308
|
"converged": "No hay preguntas pendientes. El planificador tiene lo que necesita y esta redactando el plan.",
|
|
5307
|
-
"idle": "La planificacion aun no ha comenzado. Ejecuta la planificacion desde la iniciativa
|
|
5309
|
+
"idle": "La planificacion aun no ha comenzado. Ejecuta la planificacion desde la iniciativa: primero analiza el codigo y despues te entrevista.",
|
|
5308
5310
|
"working": "El planificador esta procesando tus respuestas",
|
|
5309
5311
|
"workingHint": "Esto tarda un momento. Las preguntas de seguimiento apareceran aqui cuando termine, o empezara a redactar el plan si ya tiene suficiente.",
|
|
5312
|
+
"preparing": "Explorando el codigo antes de la entrevista",
|
|
5313
|
+
"preparingHint": "La planificacion lee primero el repositorio para no preguntarte lo que el codigo ya responde. Las preguntas apareceran aqui cuando termine.",
|
|
5310
5314
|
"failed": "La ejecucion de planificacion se detuvo",
|
|
5311
5315
|
"failedHint": "Termino antes de que el planificador pudiera responder. Tus respuestas estan guardadas; vuelve a ejecutar la planificacion desde la iniciativa.",
|
|
5312
5316
|
"answerPlaceholder": "Tu respuesta",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -3570,6 +3570,8 @@
|
|
|
3570
3570
|
"converged": "Aucune question en attente. L'intervieweur a ce qu'il lui faut et le document est en cours de rédaction.",
|
|
3571
3571
|
"working": "L'intervieweur traite vos réponses",
|
|
3572
3572
|
"workingHint": "Cela prend un instant. Les questions complémentaires apparaîtront ici une fois terminé, ou la rédaction commencera s'il en a assez.",
|
|
3573
|
+
"preparing": "Recherche sur le document avant l'entretien",
|
|
3574
|
+
"preparingHint": "L'exécution rassemble d'abord le contexte et ébauche un plan. Les questions apparaîtront ici une fois terminé.",
|
|
3573
3575
|
"failed": "L'exécution du document s'est arrêtée",
|
|
3574
3576
|
"failedHint": "Elle s'est terminée avant que l'intervieweur puisse répondre. Vos réponses sont enregistrées ; relancez la tâche du document.",
|
|
3575
3577
|
"answerPlaceholder": "Votre réponse",
|
|
@@ -5304,9 +5306,11 @@
|
|
|
5304
5306
|
"intro": "Le planificateur cadre cette initiative. Repondez a ses questions pour definir l'objectif et les contraintes, puis envoyez-les. Vous pouvez aussi lui demander de planifier maintenant avec ce qu'il a deja.",
|
|
5305
5307
|
"empty": "Aucune initiative trouvee pour ce bloc.",
|
|
5306
5308
|
"converged": "Aucune question en attente. Le planificateur dispose de ce qu'il faut et redige le plan.",
|
|
5307
|
-
"idle": "La planification n'a pas encore demarre. Lancez la planification depuis l'initiative
|
|
5309
|
+
"idle": "La planification n'a pas encore demarre. Lancez la planification depuis l'initiative : elle analyse d'abord le code, puis vous interroge.",
|
|
5308
5310
|
"working": "Le planificateur traite vos reponses",
|
|
5309
5311
|
"workingHint": "Cela prend un instant. Les questions complementaires apparaitront ici une fois termine, ou il commencera a rediger le plan s'il en a assez.",
|
|
5312
|
+
"preparing": "Exploration du code avant l'entretien",
|
|
5313
|
+
"preparingHint": "La planification lit d'abord le depot afin de ne pas vous demander ce que le code peut repondre. Les questions apparaitront ici une fois termine.",
|
|
5310
5314
|
"failed": "L'execution de planification s'est arretee",
|
|
5311
5315
|
"failedHint": "Elle s'est terminee avant que le planificateur puisse repondre. Vos reponses sont enregistrees ; relancez la planification depuis l'initiative.",
|
|
5312
5316
|
"answerPlaceholder": "Votre reponse",
|
package/i18n/locales/he.json
CHANGED
|
@@ -3581,6 +3581,8 @@
|
|
|
3581
3581
|
"converged": "אין שאלות ממתינות. למראיין יש את מה שהוא צריך והמסמך בכתיבה.",
|
|
3582
3582
|
"working": "המראיין מעבד את התשובות שלך",
|
|
3583
3583
|
"workingHint": "זה לוקח רגע. שאלות המשך יופיעו כאן בסיום, או שתתחיל כתיבת הטיוטה אם יש לו מספיק מידע.",
|
|
3584
|
+
"preparing": "המסמך נחקר לפני הראיון",
|
|
3585
|
+
"preparingHint": "ההרצה אוספת תחילה רקע ומכינה מתאר. שאלות יופיעו כאן בסיום.",
|
|
3584
3586
|
"failed": "הרצת המסמך נעצרה",
|
|
3585
3587
|
"failedHint": "היא הסתיימה לפני שהמראיין הספיק להשיב. התשובות שלך נשמרו; הריצו מחדש את משימת המסמך.",
|
|
3586
3588
|
"answerPlaceholder": "התשובה שלך",
|
|
@@ -5315,9 +5317,11 @@
|
|
|
5315
5317
|
"intro": "המתכנן ממקד את היוזמה. ענה על שאלותיו כדי לעצב את המטרה והאילוצים, ואז שלח את התשובות. אפשר גם לבקש ממנו לתכנן עכשיו עם מה שכבר יש לו.",
|
|
5316
5318
|
"empty": "לא נמצאה יוזמה עבור בלוק זה.",
|
|
5317
5319
|
"converged": "אין שאלות ממתינות. למתכנן יש את מה שנדרש והוא מנסח את התוכנית.",
|
|
5318
|
-
"idle": "התכנון עדיין לא התחיל. הרץ תכנון
|
|
5320
|
+
"idle": "התכנון עדיין לא התחיל. הרץ תכנון מהיוזמה: תחילה הוא מנתח את בסיס הקוד ולאחר מכן מראיין אותך.",
|
|
5319
5321
|
"working": "המתכנן מעבד את התשובות שלך",
|
|
5320
5322
|
"workingHint": "זה לוקח רגע. שאלות המשך יופיעו כאן בסיום, או שהוא יתחיל לנסח את התוכנית אם יש לו מספיק מידע.",
|
|
5323
|
+
"preparing": "בסיס הקוד נסרק לפני הראיון",
|
|
5324
|
+
"preparingHint": "התכנון קורא תחילה את המאגר כדי שלא תישאל על מה שהקוד יכול לענות. השאלות יופיעו כאן בסיום.",
|
|
5321
5325
|
"failed": "הרצת התכנון נעצרה",
|
|
5322
5326
|
"failedHint": "היא הסתיימה לפני שהמתכנן הספיק להשיב. התשובות שלך נשמרו; הרץ תכנון מחדש מהיוזמה.",
|
|
5323
5327
|
"answerPlaceholder": "התשובה שלך",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4294,9 +4294,11 @@
|
|
|
4294
4294
|
"intro": "Il pianificatore sta definendo l'ambito di questa iniziativa. Rispondi alle sue domande per dare forma all'obiettivo e ai vincoli, poi invia le risposte. Puoi anche chiedergli di pianificare subito con quello che ha gia.",
|
|
4295
4295
|
"empty": "Nessuna iniziativa trovata per questo blocco.",
|
|
4296
4296
|
"converged": "Nessuna domanda in sospeso. Il pianificatore ha cio che gli serve e sta redigendo il piano.",
|
|
4297
|
-
"idle": "La pianificazione non e ancora iniziata. Esegui la pianificazione dall'iniziativa
|
|
4297
|
+
"idle": "La pianificazione non e ancora iniziata. Esegui la pianificazione dall'iniziativa: prima analizza il codice, poi ti intervista.",
|
|
4298
4298
|
"working": "Il pianificatore sta elaborando le tue risposte",
|
|
4299
4299
|
"workingHint": "Richiede qualche istante. Le domande di follow-up compariranno qui al termine, oppure iniziera a redigere il piano se ha abbastanza informazioni.",
|
|
4300
|
+
"preparing": "Esplorazione del codice prima dell'intervista",
|
|
4301
|
+
"preparingHint": "La pianificazione legge prima il repository, cosi non ti viene chiesto cio che il codice puo rispondere. Le domande compariranno qui al termine.",
|
|
4300
4302
|
"failed": "L'esecuzione della pianificazione si e interrotta",
|
|
4301
4303
|
"failedHint": "Si e conclusa prima che il pianificatore potesse rispondere. Le tue risposte sono salvate; riesegui la pianificazione dall'iniziativa.",
|
|
4302
4304
|
"answerPlaceholder": "La tua risposta",
|
|
@@ -4647,6 +4649,8 @@
|
|
|
4647
4649
|
"converged": "Nessuna domanda in sospeso. L'intervistatore ha ciò che gli serve e il documento è in fase di redazione.",
|
|
4648
4650
|
"working": "L'intervistatore sta elaborando le tue risposte",
|
|
4649
4651
|
"workingHint": "Richiede qualche istante. Le domande di follow-up compariranno qui al termine, oppure inizierà la stesura se ha abbastanza informazioni.",
|
|
4652
|
+
"preparing": "Ricerca sul documento prima dell'intervista",
|
|
4653
|
+
"preparingHint": "L'esecuzione raccoglie prima il contesto e abbozza una scaletta. Le domande compariranno qui al termine.",
|
|
4650
4654
|
"failed": "L'esecuzione del documento si è interrotta",
|
|
4651
4655
|
"failedHint": "Si è conclusa prima che l'intervistatore potesse rispondere. Le tue risposte sono salvate; riesegui l'attività del documento.",
|
|
4652
4656
|
"answerPlaceholder": "La tua risposta",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -3582,6 +3582,8 @@
|
|
|
3582
3582
|
"converged": "保留中の質問はありません。インタビュアーは必要な情報を得ており、ドキュメントを下書き中です。",
|
|
3583
3583
|
"working": "インタビュアーが回答を処理しています",
|
|
3584
3584
|
"workingHint": "少し時間がかかります。完了すると追加の質問がここに表示されます。情報が十分な場合は下書きの作成を開始します。",
|
|
3585
|
+
"preparing": "インタビューの前に資料を調査しています",
|
|
3586
|
+
"preparingHint": "実行はまず背景情報を集めてアウトラインを作成します。完了すると質問がここに表示されます。",
|
|
3585
3587
|
"failed": "ドキュメントの実行が停止しました",
|
|
3586
3588
|
"failedHint": "インタビュアーが応答する前に終了しました。回答は保存されています。ドキュメントのタスクを再実行してください。",
|
|
3587
3589
|
"answerPlaceholder": "回答",
|
|
@@ -5316,9 +5318,11 @@
|
|
|
5316
5318
|
"intro": "プランナーがこのイニシアチブの範囲を検討しています。質問に答えて目標と制約を形にし、回答を送信してください。現状の情報のまま計画を作成させることもできます。",
|
|
5317
5319
|
"empty": "このブロックのイニシアチブが見つかりません。",
|
|
5318
5320
|
"converged": "保留中の質問はありません。プランナーは必要な情報を得て計画を作成しています。",
|
|
5319
|
-
"idle": "
|
|
5321
|
+
"idle": "計画はまだ開始されていません。イニシアチブから計画を実行すると、まずコードベースを分析し、その後にインタビューが行われます。",
|
|
5320
5322
|
"working": "プランナーが回答を処理しています",
|
|
5321
5323
|
"workingHint": "少し時間がかかります。完了すると追加の質問がここに表示されます。情報が十分な場合は計画の作成を開始します。",
|
|
5324
|
+
"preparing": "インタビューの前にコードベースを分析しています",
|
|
5325
|
+
"preparingHint": "計画はまずリポジトリを読み取るため、コードから分かることは質問されません。完了すると質問がここに表示されます。",
|
|
5322
5326
|
"failed": "計画の実行が停止しました",
|
|
5323
5327
|
"failedHint": "プランナーが応答する前に終了しました。回答は保存されています。イニシアチブから計画を再実行してください。",
|
|
5324
5328
|
"answerPlaceholder": "回答",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -3570,6 +3570,8 @@
|
|
|
3570
3570
|
"converged": "Brak oczekujących pytań. Ankieter ma to, czego potrzebuje, a dokument jest redagowany.",
|
|
3571
3571
|
"working": "Ankieter przetwarza Twoje odpowiedzi",
|
|
3572
3572
|
"workingHint": "To chwilę potrwa. Pytania uzupełniające pojawią się tutaj po zakończeniu, a jeśli informacji wystarczy, rozpocznie się redagowanie.",
|
|
3573
|
+
"preparing": "Badanie dokumentu przed wywiadem",
|
|
3574
|
+
"preparingHint": "Uruchomienie najpierw zbiera materiały i szkicuje konspekt. Pytania pojawią się tutaj po zakończeniu.",
|
|
3573
3575
|
"failed": "Uruchomienie dokumentu zostało przerwane",
|
|
3574
3576
|
"failedHint": "Zakończyło się, zanim ankieter zdążył odpowiedzieć. Twoje odpowiedzi są zapisane; uruchom zadanie dokumentu ponownie.",
|
|
3575
3577
|
"answerPlaceholder": "Twoja odpowiedź",
|
|
@@ -5304,9 +5306,11 @@
|
|
|
5304
5306
|
"intro": "Planista okresla zakres tej inicjatywy. Odpowiedz na pytania, aby uksztaltowac cel i ograniczenia, a nastepnie wyslij odpowiedzi. Mozesz tez poprosic go, aby zaplanowal teraz na podstawie tego, co juz ma.",
|
|
5305
5307
|
"empty": "Nie znaleziono inicjatywy dla tego bloku.",
|
|
5306
5308
|
"converged": "Brak oczekujacych pytan. Planista ma to, czego potrzebuje, i tworzy plan.",
|
|
5307
|
-
"idle": "Planowanie jeszcze sie nie rozpoczelo. Uruchom planowanie z poziomu inicjatywy,
|
|
5309
|
+
"idle": "Planowanie jeszcze sie nie rozpoczelo. Uruchom planowanie z poziomu inicjatywy: najpierw analizuje kod, a potem przeprowadza z toba wywiad.",
|
|
5308
5310
|
"working": "Planista przetwarza Twoje odpowiedzi",
|
|
5309
5311
|
"workingHint": "To chwile potrwa. Pytania uzupelniajace pojawia sie tutaj po zakonczeniu, a jesli informacji wystarczy, planista zacznie tworzyc plan.",
|
|
5312
|
+
"preparing": "Analiza kodu przed wywiadem",
|
|
5313
|
+
"preparingHint": "Planowanie najpierw czyta repozytorium, wiec nie zapyta Cie o to, co wynika z kodu. Pytania pojawia sie tutaj po zakonczeniu.",
|
|
5310
5314
|
"failed": "Uruchomienie planowania zostalo przerwane",
|
|
5311
5315
|
"failedHint": "Zakonczylo sie, zanim planista zdazyl odpowiedziec. Twoje odpowiedzi sa zapisane; uruchom planowanie ponownie z poziomu inicjatywy.",
|
|
5312
5316
|
"answerPlaceholder": "Twoja odpowiedz",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -3582,6 +3582,8 @@
|
|
|
3582
3582
|
"converged": "Bekleyen soru yok. Görüşmecinin ihtiyacı olan bilgi var ve belge taslağı hazırlanıyor.",
|
|
3583
3583
|
"working": "Görüşmeci yanıtlarınızı işliyor",
|
|
3584
3584
|
"workingHint": "Bu biraz sürer. Bittiğinde ek sorular burada görünür, yeterli bilgi varsa taslak hazırlanmaya başlar.",
|
|
3585
|
+
"preparing": "Görüşmeden önce belge araştırılıyor",
|
|
3586
|
+
"preparingHint": "Çalışma önce arka plan bilgisi toplar ve bir taslak plan çıkarır. Sorular bittiğinde burada görünür.",
|
|
3585
3587
|
"failed": "Belge çalışması durdu",
|
|
3586
3588
|
"failedHint": "Görüşmeci yanıt veremeden sona erdi. Yanıtlarınız kaydedildi; belge görevini yeniden çalıştırın.",
|
|
3587
3589
|
"answerPlaceholder": "Yanıtınız",
|
|
@@ -5316,9 +5318,11 @@
|
|
|
5316
5318
|
"intro": "Planlayici bu girisimi kapsamlandiriyor. Hedefi ve kisitlari sekillendirmek icin sorularini yanitlayin, ardindan yanitlari gonderin. Elindekiyle hemen planlamasini da isteyebilirsiniz.",
|
|
5317
5319
|
"empty": "Bu blok icin girisim bulunamadi.",
|
|
5318
5320
|
"converged": "Bekleyen soru yok. Planlayici ihtiyaci olani aldi ve plani hazirliyor.",
|
|
5319
|
-
"idle": "Planlama henuz baslamadi.
|
|
5321
|
+
"idle": "Planlama henuz baslamadi. Girisimden planlamayi calistirin: once kod tabanini analiz eder, sonra sizinle gorusur.",
|
|
5320
5322
|
"working": "Planlayici yanitlarinizi isliyor",
|
|
5321
5323
|
"workingHint": "Bu biraz surer. Bittiginde ek sorular burada gorunur, yeterli bilgi varsa plani hazirlamaya baslar.",
|
|
5324
|
+
"preparing": "Gorusmeden once kod tabani inceleniyor",
|
|
5325
|
+
"preparingHint": "Planlama once depoyu okur, boylece kodun yanitlayabilecegi seyler size sorulmaz. Sorular bittiginde burada gorunur.",
|
|
5322
5326
|
"failed": "Planlama calismasi durdu",
|
|
5323
5327
|
"failedHint": "Planlayici yanit veremeden sona erdi. Yanitlariniz kaydedildi; planlamayi girisimden yeniden calistirin.",
|
|
5324
5328
|
"answerPlaceholder": "Yanitiniz",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -3570,6 +3570,8 @@
|
|
|
3570
3570
|
"converged": "Немає запитань, що очікують. Інтерв'юер має все необхідне, і документ у процесі написання.",
|
|
3571
3571
|
"working": "Інтерв'юер опрацьовує ваші відповіді",
|
|
3572
3572
|
"workingHint": "Це займе трохи часу. Додаткові запитання з'являться тут після завершення, або почнеться написання чернетки, якщо йому достатньо даних.",
|
|
3573
|
+
"preparing": "Дослідження документа перед інтерв'ю",
|
|
3574
|
+
"preparingHint": "Запуск спершу збирає матеріали та готує план. Запитання з'являться тут після завершення.",
|
|
3573
3575
|
"failed": "Запуск документа зупинено",
|
|
3574
3576
|
"failedHint": "Він завершився, перш ніж інтерв'юер встиг відповісти. Ваші відповіді збережено; запустіть завдання документа ще раз.",
|
|
3575
3577
|
"answerPlaceholder": "Ваша відповідь",
|
|
@@ -5304,9 +5306,11 @@
|
|
|
5304
5306
|
"intro": "Планувальник окреслює цю ініціативу. Дайте відповіді на його питання, щоб сформувати мету й обмеження, а потім надішліть їх. Також можна попросити його спланувати зараз із тим, що вже є.",
|
|
5305
5307
|
"empty": "Для цього блоку ініціативу не знайдено.",
|
|
5306
5308
|
"converged": "Немає питань, що очікують. Планувальник має все необхідне й готує план.",
|
|
5307
|
-
"idle": "Планування ще не розпочато. Запустіть планування з
|
|
5309
|
+
"idle": "Планування ще не розпочато. Запустіть планування з ініціативи: спершу воно аналізує кодову базу, а потім проводить інтерв'ю.",
|
|
5308
5310
|
"working": "Планувальник опрацьовує ваші відповіді",
|
|
5309
5311
|
"workingHint": "Це займе трохи часу. Додаткові питання з'являться тут після завершення, або планувальник почне готувати план, якщо йому достатньо даних.",
|
|
5312
|
+
"preparing": "Аналіз кодової бази перед інтерв'ю",
|
|
5313
|
+
"preparingHint": "Планування спершу читає репозиторій, щоб не питати вас про те, що може відповісти код. Питання з'являться тут після завершення.",
|
|
5310
5314
|
"failed": "Запуск планування зупинено",
|
|
5311
5315
|
"failedHint": "Він завершився, перш ніж планувальник встиг відповісти. Ваші відповіді збережено; запустіть планування з ініціативи ще раз.",
|
|
5312
5316
|
"answerPlaceholder": "Ваша відповідь",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.182.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|