@cat-factory/app 0.295.0 → 0.296.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.
@@ -0,0 +1,131 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import type { BootstrapJob } from '~/types/domain'
3
+ import { useAgentRunsStore } from '~/stores/agentRuns'
4
+ import { useBootstrapRunSteps } from '~/composables/useBootstrapRunSteps'
5
+
6
+ // What this composable owns beyond the shared derivation (tested in `@cat-factory/contracts`):
7
+ // the SPA-only question of whether a run has more than one step, which is what decides whether
8
+ // the board renders a step list at all and whether the retry control offers to RESUME.
9
+
10
+ const MONOREPO: BootstrapJob['monorepo'] = {
11
+ repoGithubId: 7,
12
+ directory: 'services/payments',
13
+ repoOwner: 'acme',
14
+ repoName: 'platform',
15
+ branch: null,
16
+ }
17
+
18
+ function job(id: string, over: Partial<BootstrapJob> = {}): BootstrapJob {
19
+ return {
20
+ id,
21
+ workspaceId: 'ws_test',
22
+ referenceArchitectureId: null,
23
+ referenceArchitectureName: null,
24
+ repoName: id,
25
+ repoOwner: null,
26
+ repoUrl: null,
27
+ instructions: '',
28
+ status: 'running',
29
+ blockId: `blk_${id}`,
30
+ subtasks: null,
31
+ error: null,
32
+ failure: null,
33
+ monorepo: null,
34
+ phase: null,
35
+ // The base fixture is a new-repo run, so it takes that target's default delivery. The step
36
+ // rule never reads the field, which is why no case below overrides it: how a run's work
37
+ // lands says nothing about how many moves it takes to get there.
38
+ delivery: 'direct_push',
39
+ adoptionPlan: null,
40
+ adoptionReview: null,
41
+ prUrl: null,
42
+ createdAt: 1,
43
+ updatedAt: 1,
44
+ ...over,
45
+ }
46
+ }
47
+
48
+ /** A recorded plan; only its own status is read by the rule. */
49
+ function plan(status: 'ready' | 'unavailable'): BootstrapJob['adoptionPlan'] {
50
+ return { status } as unknown as BootstrapJob['adoptionPlan']
51
+ }
52
+
53
+ describe('useBootstrapRunSteps', () => {
54
+ let store: ReturnType<typeof useAgentRunsStore>
55
+ beforeEach(() => {
56
+ store = useAgentRunsStore()
57
+ })
58
+
59
+ it('offers nothing for a new-repo run, which has one move and so nothing to resume', () => {
60
+ store.upsertBootstrap(job('b1', { status: 'failed' }))
61
+ const { multiStep, resumeStep } = useBootstrapRunSteps('b1')
62
+ expect(multiStep.value).toBe(false)
63
+ // Null rather than 'scaffold': there is progress to keep on a monorepo run and none here,
64
+ // so the card must keep saying "retry" instead of promising a resume it cannot make.
65
+ expect(resumeStep.value).toBeNull()
66
+ })
67
+
68
+ it('marks the survey done and the review as the step a broken monorepo run is holding', () => {
69
+ store.upsertBootstrap(
70
+ job('b2', {
71
+ monorepo: MONOREPO,
72
+ phase: 'survey',
73
+ status: 'failed',
74
+ failure: { kind: 'agent' } as unknown as BootstrapJob['failure'],
75
+ adoptionPlan: plan('ready'),
76
+ }),
77
+ )
78
+ const { steps, multiStep, resumeStep } = useBootstrapRunSteps('b2')
79
+ expect(multiStep.value).toBe(true)
80
+ expect(steps.value).toEqual([
81
+ { id: 'survey', state: 'done' },
82
+ { id: 'review', state: 'failed' },
83
+ { id: 'apply', state: 'pending' },
84
+ ])
85
+ expect(resumeStep.value).toBe('review')
86
+ })
87
+
88
+ it('renders a run the reviewer STOPPED as stopped, not as a broken review step', () => {
89
+ // A stop is stored as a `failed` status with a `cancelled` kind, and this is the shape the
90
+ // card actually renders: stopping a parked run must not report the reviewer's own decision
91
+ // step back to them as a fault. The resume it offers is unchanged.
92
+ store.upsertBootstrap(
93
+ job('b2s', {
94
+ monorepo: MONOREPO,
95
+ phase: 'survey',
96
+ status: 'failed',
97
+ failure: { kind: 'cancelled' } as unknown as BootstrapJob['failure'],
98
+ adoptionPlan: plan('ready'),
99
+ }),
100
+ )
101
+ const { steps, resumeStep } = useBootstrapRunSteps('b2s')
102
+ expect(steps.value.map((step) => step.state)).toEqual(['done', 'stopped', 'pending'])
103
+ expect(resumeStep.value).toBe('review')
104
+ })
105
+
106
+ it('follows the run as live events advance it, rather than pinning the first read', () => {
107
+ // The card is open while the run moves: the review is settled and the apply dispatches, and
108
+ // the step list has to follow the store rather than the value it was mounted with.
109
+ store.upsertBootstrap(job('b3', { monorepo: MONOREPO, phase: 'survey', updatedAt: 1 }))
110
+ const { steps, resumeStep } = useBootstrapRunSteps('b3')
111
+ expect(steps.value.map((s) => s.state)).toEqual(['running', 'pending', 'pending'])
112
+ store.upsertBootstrap(
113
+ job('b3', {
114
+ monorepo: MONOREPO,
115
+ phase: 'apply',
116
+ adoptionPlan: plan('ready'),
117
+ adoptionReview: { choices: [] } as unknown as BootstrapJob['adoptionReview'],
118
+ updatedAt: 2,
119
+ }),
120
+ )
121
+ expect(steps.value.map((s) => s.state)).toEqual(['done', 'done', 'running'])
122
+ expect(resumeStep.value).toBe('apply')
123
+ })
124
+
125
+ it('answers empty for a run the store does not hold', () => {
126
+ const { steps, multiStep, resumeStep } = useBootstrapRunSteps('nope')
127
+ expect(steps.value).toEqual([])
128
+ expect(multiStep.value).toBe(false)
129
+ expect(resumeStep.value).toBeNull()
130
+ })
131
+ })
@@ -0,0 +1,37 @@
1
+ import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from 'vue'
2
+ import {
3
+ bootstrapResumeStep,
4
+ bootstrapRunSteps,
5
+ type BootstrapRunStep,
6
+ type BootstrapStepId,
7
+ } from '@cat-factory/contracts'
8
+ import { useAgentRunsStore } from '~/stores/agentRuns'
9
+
10
+ /**
11
+ * A bootstrap run projected onto its steps, for the surfaces that render them and for the
12
+ * button that resumes one.
13
+ *
14
+ * The projection itself lives in `@cat-factory/contracts` and is shared with the backend, which
15
+ * BRANCHES on the same rule (`bootstrapResume`) in `BootstrapService.retry`; this side needs only
16
+ * the step it answers with, never the state it carries. What this composable adds is the
17
+ * one SPA-side question the backend never asks: whether the run has more than one step at all.
18
+ * A new-repo bootstrap is a single move, so for it a step list restates the banner it sits under
19
+ * and "resume from…" is a promise about progress there is none of: it simply starts again.
20
+ */
21
+ export function useBootstrapRunSteps(runId: MaybeRefOrGetter<string | null | undefined>): {
22
+ /** The run's steps in order, with the reached one carrying the run's state. Empty if unknown. */
23
+ steps: ComputedRef<BootstrapRunStep[]>
24
+ /** Whether the run is a multi-step (monorepo) one: the gate every caller here needs. */
25
+ multiStep: ComputedRef<boolean>
26
+ /** The step a retry re-enters at, or null when the run is single-step or unknown. */
27
+ resumeStep: ComputedRef<BootstrapStepId | null>
28
+ } {
29
+ const agentRuns = useAgentRunsStore()
30
+ const job = computed(() => agentRuns.bootstrapById(toValue(runId)))
31
+ const steps = computed<BootstrapRunStep[]>(() => (job.value ? bootstrapRunSteps(job.value) : []))
32
+ const multiStep = computed(() => steps.value.length > 1)
33
+ const resumeStep = computed<BootstrapStepId | null>(() =>
34
+ job.value && multiStep.value ? bootstrapResumeStep(job.value) : null,
35
+ )
36
+ return { steps, multiStep, resumeStep }
37
+ }
@@ -5,6 +5,7 @@ import {
5
5
  describeGenericFailure,
6
6
  } from '~/composables/usePipelineErrorToast'
7
7
  import { ApiError } from '~/composables/api/errors'
8
+ import { BOOTSTRAP_REFERENCE_REASONS, UNAVAILABLE_REASONS } from '@cat-factory/contracts'
8
9
  import en from '../../i18n/locales/en.json'
9
10
 
10
11
  /**
@@ -304,6 +305,23 @@ describe('describeGenericFailure', () => {
304
305
  ).toBe('errors.generic.description.unexpected')
305
306
  })
306
307
 
308
+ it('gives every reason with its own copy a key that ships, and lets it beat the status class', () => {
309
+ // Derived from the vocabularies the code reads rather than pinned to a count: a new reason is
310
+ // supposed to be an ordinary addition, and an expectation that fails on every one of them
311
+ // trains the next person to re-pin it unread. What is worth asserting is the property the
312
+ // exhaustive `Record` alone cannot make, that the key it maps to actually EXISTS in the
313
+ // catalog, and that a reason wins over the status class it is attached to. Both refusals here
314
+ // reach a person who can act on them, and the generic 503 wording ("this deployment has not
315
+ // configured the capability") would send them to configure something that is already wired.
316
+ for (const reason of [...UNAVAILABLE_REASONS, ...BOOTSTRAP_REFERENCE_REASONS]) {
317
+ const failure = describeGenericFailure(
318
+ new ApiError(503, { error: { code: 'unavailable', details: { reason } } }),
319
+ )
320
+ expect(failure.descriptionKey).not.toBe('errors.generic.description.unavailable')
321
+ expect(hasKey(failure.descriptionKey), `${reason} has no copy`).toBe(true)
322
+ }
323
+ })
324
+
307
325
  it('never presents a conflict (parseConflict owns those) but still classifies safely', () => {
308
326
  // `conflict` is deliberately absent from the map, so it reads as an unrecognised code rather
309
327
  // than throwing — the conflict path intercepts it long before this function is reached.
@@ -28,7 +28,12 @@ import { createBespokeConflictToasts } from '~/composables/pipelineErrorToast/be
28
28
  // Imported by path rather than left to Nuxt's auto-import: this module is also loaded directly by
29
29
  // unit tests (and from store setup), where the auto-import globals are not installed.
30
30
  import { useCopyToClipboard } from '~/composables/useCopyToClipboard'
31
- import type { ApiErrorCode, ConflictReason, UnavailableReason } from '@cat-factory/contracts'
31
+ import type {
32
+ ApiErrorCode,
33
+ BootstrapReferenceReason,
34
+ ConflictReason,
35
+ UnavailableReason,
36
+ } from '@cat-factory/contracts'
32
37
  import { apiErrorEnvelope, apiErrorReason, apiErrorStatus } from './api/errors'
33
38
 
34
39
  /** The parsed shape of a backend conflict (`{ error: { code: 'conflict', details } }`). */
@@ -386,8 +391,16 @@ const GENERIC_DESCRIPTION_KEYS: Record<Exclude<ApiErrorCode, 'conflict'>, string
386
391
  * the reasons in {@link UNAVAILABLE_REASONS} carry their own copy, and the exhaustive `Record`
387
392
  * over that union is the drift guard: a new user-reachable 503 reason fails this typecheck until
388
393
  * it has wording.
394
+ *
395
+ * `BootstrapReferenceReason` joins it because the same argument reaches one 422: a bootstrap
396
+ * refused for its reference architecture is not "the request was malformed", it names a specific
397
+ * entry a specific person can go and fix. The launch dialog handles that one itself, since it can
398
+ * open the entry. This is what every OTHER caller of the funnel is told instead of the status
399
+ * class's generic wording, above all a retry driven from the run card.
389
400
  */
390
- const UNAVAILABLE_DESCRIPTION_KEYS: Record<UnavailableReason, string> = {
401
+ const REASON_DESCRIPTION_KEYS: Record<UnavailableReason | BootstrapReferenceReason, string> = {
402
+ reference_repo_not_found: 'errors.reason.description.reference_repo_not_found',
403
+ reference_repo_unreadable: 'errors.reason.description.reference_repo_unreadable',
391
404
  binary_generators_unreachable: 'errors.unavailable.description.binary_generators_unreachable',
392
405
  foundational_builtins_unreachable:
393
406
  'errors.unavailable.description.foundational_builtins_unreachable',
@@ -445,7 +458,7 @@ export function describeGenericFailure(error: unknown): GenericFailure {
445
458
  // A REASON that has its own copy wins over the status class's, through the same widened-alias
446
459
  // read and for the same reason: a `reason` this build doesn't know must resolve to `undefined`
447
460
  // and fall through, never narrow the wire string to the union by casting.
448
- const byReason: Readonly<Record<string, string | undefined>> = UNAVAILABLE_DESCRIPTION_KEYS
461
+ const byReason: Readonly<Record<string, string | undefined>> = REASON_DESCRIPTION_KEYS
449
462
  const reason = apiErrorReason(error)
450
463
  const mapped =
451
464
  (reason ? byReason[reason] : undefined) ?? (envelope?.code ? byCode[envelope.code] : undefined)
@@ -158,6 +158,18 @@ export const useAgentRunsStore = defineStore('agentRuns', () => {
158
158
  return map
159
159
  })
160
160
 
161
+ /**
162
+ * One bootstrap run by its RUN id.
163
+ *
164
+ * The counterpart to `execution.getInstance`, and it exists for the same surfaces: anything
165
+ * that holds a run id and needs the run WHOLE rather than the coarse {@link byBlock} summary:
166
+ * the observability panel's header, and the step list a card renders. A retry mints a NEW id,
167
+ * so unlike the block-keyed reads below this one needs nothing of the list's ordering.
168
+ */
169
+ function bootstrapById(runId: string | null | undefined): BootstrapJob | undefined {
170
+ return runId ? bootstrapJobs.value.find((job) => job.id === runId) : undefined
171
+ }
172
+
161
173
  /**
162
174
  * The parked monorepo bootstrap for a block, when it is waiting on an adoption review.
163
175
  *
@@ -221,6 +233,7 @@ export const useAgentRunsStore = defineStore('agentRuns', () => {
221
233
 
222
234
  return {
223
235
  bootstrapJobs,
236
+ bootstrapById,
224
237
  awaitingReview,
225
238
  submitAdoptionReview,
226
239
  hydrate,
@@ -0,0 +1,60 @@
1
+ import type { BootstrapStepState } from '@cat-factory/contracts'
2
+
3
+ // Display metadata for a bootstrap run's step states, the `catalog.ts` idea at the scale of one
4
+ // vocabulary: the icon and the two tones that render a state, in ONE record rather than three
5
+ // parallel ones keyed alike. Three had to be kept in step by hand, which is a silent way to give
6
+ // a newly added state (`stopped`, say) a red icon and calm text.
7
+ //
8
+ // Module scope rather than a component's `<script setup>`, where a top-level const is rebuilt for
9
+ // every instance: the step list renders on every in-progress, parked and failed bootstrap card on
10
+ // the board, plus the inspector and the failure card.
11
+
12
+ /** How one step state renders: its icon, the icon's tone, and the label's. */
13
+ export interface BootstrapStepStyle {
14
+ icon: string
15
+ iconClass: string
16
+ labelClass: string
17
+ }
18
+
19
+ /**
20
+ * The style per state. `stopped` is deliberately NOT the failure red: a run someone stopped is
21
+ * stored as a failure without being one, and the step they stopped in is usually the review,
22
+ * whose only actor is the reviewer themselves.
23
+ */
24
+ export const BOOTSTRAP_STEP_STYLE: Record<BootstrapStepState, BootstrapStepStyle> = {
25
+ pending: {
26
+ icon: 'i-lucide-circle',
27
+ iconClass: 'text-slate-500',
28
+ labelClass: 'text-slate-500',
29
+ },
30
+ running: {
31
+ icon: 'i-lucide-loader-circle',
32
+ iconClass: 'animate-spin text-amber-400',
33
+ labelClass: 'text-amber-100',
34
+ },
35
+ awaiting_review: {
36
+ icon: 'i-lucide-user-check',
37
+ iconClass: 'text-amber-400',
38
+ labelClass: 'text-amber-100',
39
+ },
40
+ done: {
41
+ icon: 'i-lucide-check-circle-2',
42
+ iconClass: 'text-emerald-400',
43
+ labelClass: 'text-slate-400',
44
+ },
45
+ failed: {
46
+ icon: 'i-lucide-alert-triangle',
47
+ iconClass: 'text-rose-400',
48
+ labelClass: 'text-rose-200',
49
+ },
50
+ stopped: {
51
+ icon: 'i-lucide-circle-stop',
52
+ iconClass: 'text-slate-400',
53
+ labelClass: 'text-slate-300',
54
+ },
55
+ unknown: {
56
+ icon: 'i-lucide-help-circle',
57
+ iconClass: 'text-slate-400',
58
+ labelClass: 'text-slate-400',
59
+ },
60
+ }
@@ -1,5 +1,10 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { PIPELINE_PURPOSES, purposeAllowsAgentCategory } from '@cat-factory/contracts'
2
+ import {
3
+ MONOREPO_ADOPTION_AGENT_KIND,
4
+ PIPELINE_PURPOSES,
5
+ purposeAllowsAgentCategory,
6
+ REPO_BOOTSTRAP_AGENT_KIND,
7
+ } from '@cat-factory/contracts'
3
8
  import type { AgentKind, BlockStatus, BlockType } from '~/types/domain'
4
9
  import { narrowAgentPalette } from '~/utils/agentPalette'
5
10
  import {
@@ -78,6 +83,17 @@ describe('catalog', () => {
78
83
  }
79
84
  })
80
85
 
86
+ it('names the kinds a bootstrap run files its telemetry under', () => {
87
+ // The backend stamps these two on a bootstrap run's metric, snapshot and tool-call rows, and
88
+ // the observability panel groups by kind. Unnamed here they roll up under the generic "Agent"
89
+ // fallback: the survey's model calls and the apply container's under one unlabelled heading,
90
+ // on the one panel whose job is telling them apart. Asserted through the same constants the
91
+ // backend imports, so this cannot pass against a stale spelling.
92
+ for (const kind of [REPO_BOOTSTRAP_AGENT_KIND, MONOREPO_ADOPTION_AGENT_KIND]) {
93
+ expect(agentKindMeta(kind).label, `${kind} falls back to the unnamed agent`).not.toBe('Agent')
94
+ }
95
+ })
96
+
81
97
  it('classifies every built-in kind into a tier', () => {
82
98
  // The palette / model-preset surfaces open on `basic`, so a built-in that forgot its tier
83
99
  // would silently fall to the DEFAULT (intermediate) and vanish from the default view for
@@ -8,7 +8,11 @@ import type {
8
8
  TaskTypeMeta,
9
9
  } from '~/types/domain'
10
10
  import type { BadgeColor } from '~/utils/badge'
11
- import { isBuiltinGatableKind } from '@cat-factory/contracts'
11
+ import {
12
+ isBuiltinGatableKind,
13
+ MONOREPO_ADOPTION_AGENT_KIND,
14
+ REPO_BOOTSTRAP_AGENT_KIND,
15
+ } from '@cat-factory/contracts'
12
16
 
13
17
  /** Simple unique id helper (fine for a client-only prototype). */
14
18
  export function uid(prefix = 'id'): string {
@@ -636,6 +640,33 @@ export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
636
640
  'Re-examines a single challenged PR-review finding against the full source, then upholds ' +
637
641
  '(strengthening it) or retracts it with a justification. Configurable separately from the reviewer.',
638
642
  },
643
+ // The two agent kinds a REPO BOOTSTRAP run files its telemetry under. Neither is placeable
644
+ // and neither is a model-routing key (the bootstrapper runs on the `architect` routing, the
645
+ // advisor on the workspace default): they are here so the observability panel a bootstrap run
646
+ // opens names what actually ran. Without them both roll up as the generic "Agent" fallback,
647
+ // which puts the survey's model calls and the apply container's under one unnamed heading on
648
+ // the one panel whose job is telling them apart.
649
+ //
650
+ // Keyed off the contracts constants the BACKEND stamps on those rows, never a second spelling:
651
+ // a rename that missed one side would fall back to the unnamed heading with nothing failing.
652
+ [REPO_BOOTSTRAP_AGENT_KIND]: {
653
+ kind: REPO_BOOTSTRAP_AGENT_KIND,
654
+ tier: 'advanced',
655
+ label: 'Repo Bootstrapper',
656
+ icon: 'i-lucide-package-plus',
657
+ color: '#f59e0b',
658
+ description:
659
+ 'Scaffolds a new repository from a reference architecture, or writes a new service into a monorepo and opens the pull request.',
660
+ },
661
+ [MONOREPO_ADOPTION_AGENT_KIND]: {
662
+ kind: MONOREPO_ADOPTION_AGENT_KIND,
663
+ tier: 'advanced',
664
+ label: 'Adoption Advisor',
665
+ icon: 'i-lucide-scale',
666
+ color: '#f59e0b',
667
+ description:
668
+ 'Reads a monorepo and the reference template and proposes what a new service should adopt from each. Its suggestion is the one a human settles before anything is written.',
669
+ },
639
670
  // The Initiative Planning pipeline's steps. Only runnable on an initiative
640
671
  // block (pl_initiative — enforced by the engine), so they are display-metadata
641
672
  // system kinds, never palette archetypes. The analyst runs FIRST, ahead of the
@@ -3195,7 +3195,8 @@
3195
3195
  "retrying": "Wird wiederholt…",
3196
3196
  "history": {
3197
3197
  "previousErrors": "{count} vorheriger Fehler | {count} vorherige Fehler"
3198
- }
3198
+ },
3199
+ "resumeBootstrap": "Fortsetzen ab: {step}"
3199
3200
  },
3200
3201
  "stop": {
3201
3202
  "label": "Stoppen",
@@ -3814,7 +3815,8 @@
3814
3815
  "cacheRead": "{tokens} aus dem Cache gelesen",
3815
3816
  "cacheReadHint": "Eingabe-Tokens, die aus dem Cache des Providers bedient wurden (etwa 0,1x der Preis frischer Eingabe-Tokens)",
3816
3817
  "cacheWrite": "{tokens} in den Cache geschrieben",
3817
- "cacheWriteHint": "Eingabe-Tokens, die in den Cache des Providers geschrieben wurden (1,25x bis 2x der Preis frischer Eingabe-Tokens)"
3818
+ "cacheWriteHint": "Eingabe-Tokens, die in den Cache des Providers geschrieben wurden (1,25x bis 2x der Preis frischer Eingabe-Tokens)",
3819
+ "costNoRollup": "Nicht bepreist: Die Schätzung wird aus der Aufstellung pro Schritt gebildet, und dieser Lauf hat keine. Was seine Aufrufe verbraucht haben, steht unten."
3818
3820
  },
3819
3821
  "phase": {
3820
3822
  "title": "Wohin die Tokens geflossen sind",
@@ -3829,7 +3831,8 @@
3829
3831
  "costHint": "Geschätzte Kosten der Token dieser Phase zu Listenpreisen. Ein Strich bedeutet, dass diese Installation keinen Satz für das ausgeführte Modell kennt.",
3830
3832
  "carryCostHint": "Wie stark jede Phase die nachfolgenden Runden belastet hat: ihr Kontext einmal für jede spätere Runde gezählt, die ihn erneut senden musste. Vergleichen Sie die Phasen eines Laufs miteinander; für sich allein sagt die Zahl nichts aus. Eine spät laufende Phase schleppt wenig mit, wie viel sie auch verbraucht hat; lesen Sie diese Spalte daher zusammen mit den Tokens daneben.",
3831
3833
  "unattributed": "Nicht zugeordnet",
3832
- "unattributedHint": "von einem Kanal erfasst, der keine Phase meldet"
3834
+ "unattributedHint": "von einem Kanal erfasst, der keine Phase meldet",
3835
+ "noRollup": "Dieser Lauf hat keine Pipeline-Schritte, daher gibt es keine Aufstellung nach Phasen: Seine Modellaufrufe sind unten erfasst, aber nichts gruppiert oder bepreist sie."
3833
3836
  },
3834
3837
  "metricsBar": {
3835
3838
  "calls": "{count} Aufruf | {count} Aufrufe",
@@ -3937,7 +3940,8 @@
3937
3940
  "result": "Ergebnis",
3938
3941
  "dropped": "{chars} Zeichen bei der Erfassung verworfen",
3939
3942
  "truncated": "Es werden die ersten {shown} Aufrufe dieses Laufs angezeigt. Die Zahlen oben gelten für den gesamten Lauf; filtere auf die Fehlschläge, um alle zu sehen.",
3940
- "failuresTruncated": "Es werden die ersten {shown} fehlgeschlagenen Aufrufe angezeigt. Die Zahl oben gilt für den gesamten Lauf."
3943
+ "failuresTruncated": "Es werden die ersten {shown} fehlgeschlagenen Aufrufe angezeigt. Die Zahl oben gilt für den gesamten Lauf.",
3944
+ "surveyReadsElsewhere": "Die Lesevorgänge der Erhebung stehen nicht hier: Sie erkundet über den begrenzten Leser der Plattform, und jeder Lesevorgang ist im Übernahmeprotokoll des Laufs erfasst. Unten stehen die Tool-Aufrufe des Containers, der den Service geschrieben hat."
3941
3945
  }
3942
3946
  },
3943
3947
  "platformObservability": {
@@ -5046,7 +5050,14 @@
5046
5050
  "label": "Referenzarchitektur",
5047
5051
  "description": "Das verwaltete Basis-Repo zum Klonen und Anpassen.",
5048
5052
  "empty": "Noch keine Referenzarchitekturen. Fügen Sie unten eine hinzu oder wechseln Sie zu 'Von Grund auf'.",
5049
- "placeholder": "Referenzarchitektur wählen"
5053
+ "placeholder": "Referenzarchitektur wählen",
5054
+ "refusal": {
5055
+ "title": "Diese Referenzarchitektur konnte nicht verwendet werden",
5056
+ "notFound": "{repo} ist über die Quellcodeverwaltungs-Verbindung dieses Workspace nicht sichtbar. Entweder nennt dieser Eintrag das falsche Repository, oder die Verbindung hat keinen Zugriff darauf. Es wurde nichts angelegt: Korrigieren Sie den Eintrag und starten Sie erneut, alles andere in diesem Formular bleibt erhalten.",
5057
+ "unreadable": "{repo} konnte gerade nicht gelesen werden, deshalb wurde nichts angelegt. Es ist nichts falsch konfiguriert: Starten Sie erneut, sobald die Verbindung wieder steht, alles andere in diesem Formular bleibt erhalten.",
5058
+ "unnamedRepo": "Das Repository der Referenzarchitektur",
5059
+ "edit": "Diese Referenzarchitektur bearbeiten"
5060
+ }
5050
5061
  },
5051
5062
  "targetRepo": {
5052
5063
  "label": "Name des Ziel-Repositorys",
@@ -5250,7 +5261,26 @@
5250
5261
  "approvedDesc": "Der Agent schreibt jetzt {directory} und öffnet einen Pull Request.",
5251
5262
  "failed": "Prüfung konnte nicht abgeschickt werden"
5252
5263
  }
5253
- }
5264
+ },
5265
+ "steps": {
5266
+ "title": "Bootstrap-Schritte",
5267
+ "name": {
5268
+ "scaffold": "Repository aufsetzen",
5269
+ "survey": "Monorepo und Vorlage untersuchen",
5270
+ "review": "Deine Übernahme-Entscheidungen",
5271
+ "apply": "Service schreiben und Pull Request öffnen"
5272
+ },
5273
+ "state": {
5274
+ "pending": "Nicht begonnen",
5275
+ "running": "Läuft",
5276
+ "awaiting_review": "Wartet auf dich",
5277
+ "done": "Fertig",
5278
+ "failed": "Fehlgeschlagen",
5279
+ "stopped": "Gestoppt",
5280
+ "unknown": "Status nicht lesbar"
5281
+ }
5282
+ },
5283
+ "runKind": "Repository-Bootstrap"
5254
5284
  },
5255
5285
  "fragments": {
5256
5286
  "panel": {
@@ -6082,6 +6112,12 @@
6082
6112
  "service_catalog_response_too_large": "Das Entwicklerportal hat mit mehr Daten geantwortet, als diese Plattform in einer Antwort aufnimmt. Öffne die Servicekatalog-Einstellungen und senke das Service-Limit oder deaktiviere den Import von Schnittstellendefinitionen, und importiere dann erneut."
6083
6113
  }
6084
6114
  },
6115
+ "reason": {
6116
+ "description": {
6117
+ "reference_repo_not_found": "Das Repository hinter der Referenzarchitektur dieses Laufs ist über die Quellcodeverwaltungs-Verbindung dieses Workspace nicht sichtbar. Entweder nennt der Eintrag das falsche Repository, oder die Verbindung hat keinen Zugriff darauf: Korrigieren Sie die Referenzarchitektur oder erteilen Sie den Zugriff, und versuchen Sie es erneut.",
6118
+ "reference_repo_unreadable": "Das Repository hinter der Referenzarchitektur dieses Laufs konnte gerade nicht gelesen werden, deshalb wurde der Lauf nicht gegen eine Vorlage gestartet, die er möglicherweise nicht klonen kann. Es ist nichts falsch konfiguriert und keine Änderung nötig: versuchen Sie es erneut, sobald die Verbindung wieder steht."
6119
+ }
6120
+ },
6085
6121
  "action": {
6086
6122
  "retryFailed": "Wiederholung fehlgeschlagen",
6087
6123
  "startFailed": "Start fehlgeschlagen",
@@ -464,6 +464,10 @@
464
464
  "@previousErrors": {
465
465
  "description": "Count-based tally of a run's earlier failed attempts, rendered as e.g. '3 previous errors' (count is always >= 1). Provide ALL plural forms your language needs (English has 2; Polish/Ukrainian need 3 - one/few/many - via the custom pluralRules in i18n.config.ts)."
466
466
  }
467
+ },
468
+ "resumeBootstrap": "Resume from: {step}",
469
+ "@resumeBootstrap": {
470
+ "description": "Retry label for a multi-step (monorepo) bootstrap, which resumes at the step the run reached rather than starting over. {step} is one of the bootstrap.steps.name values."
467
471
  }
468
472
  },
469
473
  "stop": {
@@ -705,6 +709,12 @@
705
709
  "service_catalog_response_too_large": "The developer portal answered with more data than this platform will hold in one response. Open the service-catalog settings and lower the service cap, or turn off importing interface definitions, then import again."
706
710
  }
707
711
  },
712
+ "reason": {
713
+ "description": {
714
+ "reference_repo_not_found": "The repository behind this run's reference architecture cannot be seen through this workspace's source-control connection. Either the entry names the wrong repository, or the connection has not been granted access to it: correct the reference architecture, or grant it access, then try again.",
715
+ "reference_repo_unreadable": "The repository behind this run's reference architecture could not be read just now, so the run was not started against a template it might be unable to clone. Nothing here is misconfigured and no change is needed: try again once the source-control connection recovers."
716
+ }
717
+ },
708
718
  "action": {
709
719
  "retryFailed": "Retry failed",
710
720
  "startFailed": "Failed to start",
@@ -1872,7 +1882,8 @@
1872
1882
  "cacheRead": "{tokens} cache read",
1873
1883
  "cacheReadHint": "Input tokens served from the provider's cache (about 0.1x the price of fresh input)",
1874
1884
  "cacheWrite": "{tokens} cache write",
1875
- "cacheWriteHint": "Input tokens written into the provider's cache (1.25x to 2x the price of fresh input)"
1885
+ "cacheWriteHint": "Input tokens written into the provider's cache (1.25x to 2x the price of fresh input)",
1886
+ "costNoRollup": "Not priced: the estimate is folded from a run's per-step rollup, and this run has none. What its calls consumed is listed below."
1876
1887
  },
1877
1888
  "phase": {
1878
1889
  "title": "Where the tokens went",
@@ -1887,7 +1898,8 @@
1887
1898
  "costHint": "Estimated cost of this phase's tokens at list rates. A dash means this deployment has no rate for the model that ran.",
1888
1899
  "carryCostHint": "How much each phase burdened the turns that came after it: its context counted once for every later turn that had to re-send it. Compare a run's phases with each other; on its own the number means nothing. A phase that runs late carries little however much it spent, so read this column alongside the tokens beside it.",
1889
1900
  "unattributed": "Unattributed",
1890
- "unattributedHint": "recorded by a channel that reports no phase"
1901
+ "unattributedHint": "recorded by a channel that reports no phase",
1902
+ "noRollup": "This run has no pipeline steps, so there is no per-phase rollup to fold: its model calls are recorded below, but nothing groups or prices them."
1891
1903
  },
1892
1904
  "metricsBar": {
1893
1905
  "calls": "{count} call | {count} calls",
@@ -1995,7 +2007,8 @@
1995
2007
  "result": "Result",
1996
2008
  "dropped": "{chars} characters dropped at capture",
1997
2009
  "truncated": "Showing the first {shown} calls of this run. The counts above are for the whole run; narrow to the failures to see all of them.",
1998
- "failuresTruncated": "Showing the first {shown} failing calls. The count above is for the whole run."
2010
+ "failuresTruncated": "Showing the first {shown} failing calls. The count above is for the whole run.",
2011
+ "surveyReadsElsewhere": "The survey's own reads are not here: it explores through the platform's bounded reader, and every read it made is recorded on the run's adoption transcript. Below are the tool calls of the container that wrote the service."
1999
2012
  }
2000
2013
  },
2001
2014
  "platformObservability": {
@@ -7236,7 +7249,14 @@
7236
7249
  "label": "Reference architecture",
7237
7250
  "description": "The managed base repo to clone and adapt.",
7238
7251
  "empty": "No reference architectures yet. Add one below, or switch to 'From scratch'.",
7239
- "placeholder": "Choose a reference architecture"
7252
+ "placeholder": "Choose a reference architecture",
7253
+ "refusal": {
7254
+ "title": "This reference architecture could not be used",
7255
+ "notFound": "{repo} cannot be seen through this workspace's source-control connection. Either this entry names the wrong repository, or the connection has not been granted access to it. Nothing was created, so correct the entry and launch again: everything else on this form is kept.",
7256
+ "unreadable": "{repo} could not be read just now, so nothing was created. Nothing here is misconfigured: launch again once the source-control connection recovers, and everything else on this form is kept.",
7257
+ "unnamedRepo": "The reference architecture's repository",
7258
+ "edit": "Edit this reference architecture"
7259
+ }
7240
7260
  },
7241
7261
  "targetRepo": {
7242
7262
  "label": "Target repository name",
@@ -7440,6 +7460,31 @@
7440
7460
  "approvedDesc": "The agent is now writing {directory} and will open a pull request.",
7441
7461
  "failed": "Could not submit the review"
7442
7462
  }
7463
+ },
7464
+ "steps": {
7465
+ "title": "Bootstrap steps",
7466
+ "name": {
7467
+ "scaffold": "Scaffold the repository",
7468
+ "survey": "Survey the monorepo and the template",
7469
+ "review": "Your adoption decisions",
7470
+ "apply": "Write the service and open the pull request"
7471
+ },
7472
+ "state": {
7473
+ "pending": "Not started",
7474
+ "running": "Running",
7475
+ "awaiting_review": "Waiting for you",
7476
+ "done": "Done",
7477
+ "failed": "Failed",
7478
+ "stopped": "Stopped",
7479
+ "unknown": "State unreadable"
7480
+ }
7481
+ },
7482
+ "@steps": {
7483
+ "description": "The moves a bootstrap run is made of. A new-repo bootstrap is only `scaffold`; a monorepo bootstrap is survey → your decisions → apply. `state.stopped` is a run a person stopped, which is stored as a failure without being one; `state.unknown` is for a stored run status this build no longer defines."
7484
+ },
7485
+ "runKind": "Repo bootstrap",
7486
+ "@runKind": {
7487
+ "description": "The kind of run the observability panel is showing, in the line under its title where a task run names its pipeline instead. Keep it short."
7443
7488
  }
7444
7489
  },
7445
7490
  "initiative": {