@cat-factory/app 0.174.0 → 0.176.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 CHANGED
@@ -56,10 +56,10 @@ over the WebSocket. How that sync works is written up in
56
56
  ## Interface modes (basic / advanced)
57
57
 
58
58
  The SPA renders at one of two **interface tiers**. `basic` (the default) is the everyday
59
- surface: the run/pipeline options that only exist to override a workspace-level default are
60
- left at that default, and the nav is trimmed to the destinations that are the **only route**
61
- to their capability. `advanced` shows everything. The tier resolves in a fixed order, first
62
- match wins:
59
+ **delivery** surface plan work on a board, run it, review and merge it: the run/pipeline
60
+ options that only exist to override a workspace-level default are left at that default, and
61
+ the nav is trimmed to what that loop needs. `advanced` shows everything. The tier resolves in
62
+ a fixed order, first match wins:
63
63
 
64
64
  1. **`NUXT_PUBLIC_UI_MODE`** (`basic` | `advanced`) — the deployment pin. Like
65
65
  `NUXT_PUBLIC_API_BASE` it is baked in at **build** time (`ssr: false`), and while it is
@@ -82,20 +82,32 @@ hoc where it can be avoided:
82
82
  - **A nav destination** declares `advanced: true` in `app/modular/nav-contributions.ts`. The
83
83
  shared `navSlotFilter` drops it in basic mode across all three shells (sidebar, command
84
84
  palette, toolbar), independently of its RBAC `gate` — both must pass. A consumer module's
85
- own contributions take the same flag. The bar for setting it is **route count, not how
86
- advanced the surface feels**: hiding the only way to reach a capability removes the
87
- capability from the tier, while hiding a shortcut into a surface a basic destination also
88
- opens removes nothing. So the flag belongs on a surface that sits beside the delivery path
89
- (Sandbox, Kaizen), on a palette shortcut into a Workspace-settings tab, or on a knob the
90
- Integrations hub already offersand never on a sole route (the pipeline builder, the
91
- fragment library, the infrastructure/PREnv windows, the operator + reports views).
92
- `nav-contributions.spec.ts` pins the advanced set against a table of each item's
93
- alternative route, so promoting one forces that claim to be written down.
85
+ own contributions take the same flag. The bar is **whether the everyday delivery loop needs
86
+ it**, and marking an item does one of two distinguishable things:
87
+ - **Reached another way** a shortcut whose surface a basic destination also opens, so
88
+ nothing is lost (the Merge / Service-best-practices palette entries into Workspace
89
+ settings, the local-models knob the Integrations hub already offers).
90
+ - **Out of the tier** the sole route, hidden on purpose, so the capability is _absent_
91
+ from basic mode and the tier switch is the way to it (Sandbox, Kaizen, repo bootstrap,
92
+ and the deployment-wide operator + reports rollups).
93
+
94
+ Sole-route items stay in basic when the delivery loop runs on them: the pipeline builder,
95
+ add-from-repo, the fragment library, the infrastructure/PREnv windows, and the workspace /
96
+ model configuration a run actually reads. `nav-contributions.spec.ts` pins the advanced set
97
+ against a table naming each item's kind and reason, so promoting one forces that claim to be
98
+ written down rather than assumed.
99
+
94
100
  - **A less-used option inside a surface** reads `useUiModeStore().isAdvanced`. Hide, never
95
101
  disable, and only ever hide an OVERRIDE: what remains must be exactly the default the hidden
96
102
  field would have shown, so a basic-mode user never gets different behaviour from an advanced
97
103
  one — only fewer choices. An input nothing else supplies (the pipeline, the apriori branches)
98
104
  stays in both tiers however advanced it feels.
105
+ - **A whole AUTHORING affordance** may be tier-scoped the same way — the frame header's
106
+ recurring-schedule and initiative buttons are advanced-only — but only while the tier hides
107
+ the ability to CREATE, never the ability to SEE. Existing state has to stay legible in basic
108
+ mode through its normal surfaces (a live schedule still badges its task card and opens its
109
+ inspector panel; an initiative is still a block on the board with its own inspector), or the
110
+ tier turns into a way for a user to be acted on by configuration they cannot find.
99
111
  - **An override control on an EXISTING entity gates on `showOverrideField(isAdvanced, …values)`**
100
112
  (`app/utils/uiMode.ts`) rather than on `isAdvanced` alone. Hiding an override is only safe
101
113
  while it is unset — always true for a creation form, never guaranteed for a block that a
@@ -24,6 +24,7 @@ const agentRuns = useAgentRunsStore()
24
24
  const services = useServicesStore()
25
25
  const reviews = useReviewStage()
26
26
  const access = useWorkspaceAccess()
27
+ const uiMode = useUiModeStore()
27
28
  const { t } = useI18n()
28
29
  const { lod } = useSemanticZoom()
29
30
  // Coarse-pointer (touch) bumps the frame-header actions from `xs` to `sm` so
@@ -161,6 +162,13 @@ function addRecurring() {
161
162
  ui.openAddRecurring(props.id)
162
163
  }
163
164
 
165
+ // Hunt this service's tracker board for a bug worth picking up. Scoped to THIS frame, so
166
+ // an adopted candidate lands here rather than wherever the board's first frame happens to be.
167
+ function huntBugs() {
168
+ ui.expandFrame(props.id)
169
+ ui.openBugHunt(null, props.id)
170
+ }
171
+
164
172
  function createInitiative() {
165
173
  ui.expandFrame(props.id)
166
174
  ui.openCreateInitiative(props.id)
@@ -491,8 +499,18 @@ const ITEM_ICON: Record<string, string> = {
491
499
  :title="t('board.frame.createTaskFromIssueTitle')"
492
500
  @click.stop="createTaskFromIssue"
493
501
  />
502
+ <!-- Recurring pipelines + initiatives are ADVANCED-tier authoring: both plan
503
+ work rather than do it (a schedule that fires runs on a cadence, an
504
+ initiative that groups tasks under a goal), and the basic frame header is
505
+ the most-used control strip on the board. Hiding the CREATE affordance
506
+ removes neither's existing state from basic mode — a live schedule still
507
+ badges its task card and opens its inspector panel, and an initiative is
508
+ still a block on the board with its own inspector — so what a basic-mode
509
+ user loses is the ability to author a new one, not sight of one. -->
494
510
  <UButton
511
+ v-if="uiMode.isAdvanced"
495
512
  class="nodrag"
513
+ data-testid="frame-add-recurring"
496
514
  :size="isTouch ? 'sm' : 'xs'"
497
515
  variant="ghost"
498
516
  color="neutral"
@@ -501,6 +519,7 @@ const ITEM_ICON: Record<string, string> = {
501
519
  @click.stop="addRecurring"
502
520
  />
503
521
  <UButton
522
+ v-if="uiMode.isAdvanced"
504
523
  class="nodrag"
505
524
  data-testid="frame-add-initiative"
506
525
  :size="isTouch ? 'sm' : 'xs'"
@@ -510,6 +529,17 @@ const ITEM_ICON: Record<string, string> = {
510
529
  :title="t('board.frame.createInitiativeTitle')"
511
530
  @click.stop="createInitiative"
512
531
  />
532
+ <UButton
533
+ v-if="tasks.anyOffered"
534
+ class="nodrag"
535
+ data-testid="frame-hunt-bugs"
536
+ :size="isTouch ? 'sm' : 'xs'"
537
+ variant="ghost"
538
+ color="neutral"
539
+ icon="i-lucide-radar"
540
+ :title="t('board.frame.huntBugsTitle')"
541
+ @click.stop="huntBugs"
542
+ />
513
543
  </template>
514
544
  <UButton
515
545
  class="nodrag"
@@ -1,4 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import { showOverrideField } from '~/utils/uiMode'
3
+
2
4
  // The Integrations hub: a single modal that lists every external system the WORKSPACE can
3
5
  // enable or link in. Each row reuses the existing per-integration panel handlers on the `ui`
4
6
  // store (so the integrations themselves are unchanged); opening one closes the hub and
@@ -25,6 +27,7 @@ const publicApiKeys = usePublicApiKeysStore()
25
27
  const userSecrets = useUserSecretsStore()
26
28
  const apiKeys = useApiKeysStore()
27
29
  const workspace = useWorkspaceStore()
30
+ const uiMode = useUiModeStore()
28
31
 
29
32
  // True when the per-user "My setup" hub is reachable (UserMenu renders only when signed in).
30
33
  // When false (auth disabled / local mode) we fold the personal rows back into this hub so
@@ -268,7 +271,17 @@ const groups = computed<IntegrationGroup[]>(() => {
268
271
  // Gated like every other backend-toggleable system: hidden until a probe confirms
269
272
  // the observability module is enabled (`available === true`), so a disabled backend
270
273
  // doesn't show a dead "Connect" row that only 503s.
271
- if (releaseHealth.available) {
274
+ //
275
+ // Also an ADVANCED-tier row. Post-release health is the one integration here that acts
276
+ // AFTER delivery rather than during it — it watches monitors once a release ships and can
277
+ // spawn an on-call agent — so it sits outside the everyday loop the basic tier serves.
278
+ // Gated with `showOverrideField` rather than a bare `isAdvanced`: an already-connected
279
+ // Datadog is live behaviour on this workspace, and a tier must never conceal a connection a
280
+ // basic-mode user would then have no way to inspect or disconnect.
281
+ if (
282
+ releaseHealth.available &&
283
+ showOverrideField(uiMode.isAdvanced, releaseHealth.connection.connected || null)
284
+ ) {
272
285
  out.push({
273
286
  title: t('layout.integrationsHub.groups.observability'),
274
287
  items: [
@@ -13,7 +13,6 @@ const pipelines = usePipelinesStore()
13
13
  const execution = useExecutionStore()
14
14
  const ui = useUiStore()
15
15
  const documents = useDocumentsStore()
16
- const tasks = useTasksStore()
17
16
  const fragments = useFragmentsStore()
18
17
  const agentRuns = useAgentRunsStore()
19
18
  const github = useGitHubStore()
@@ -450,33 +449,9 @@ const showOriginalDescription = ref(false)
450
449
  >
451
450
  {{ block!.pullRequest!.branch }}
452
451
  </UButton>
453
- <UButton
454
- v-if="tasks.available"
455
- color="neutral"
456
- variant="soft"
457
- size="xs"
458
- icon="i-lucide-ticket"
459
- @click="ui.openTaskImport()"
460
- >
461
- {{
462
- tasks.anyOffered
463
- ? t('panels.inspector.importIssue')
464
- : t('panels.inspector.connectTracker')
465
- }}
466
- </UButton>
467
- <!-- Hunt this service's tracker board for a bug worth picking up. Scoped to the
468
- selected container, so an adopted candidate lands here rather than wherever the
469
- board's first frame happens to be. -->
470
- <UButton
471
- v-if="isContainer && tasks.anyOffered"
472
- color="neutral"
473
- variant="soft"
474
- size="xs"
475
- icon="i-lucide-radar"
476
- @click="ui.openBugHunt(null, block!.id)"
477
- >
478
- {{ t('panels.inspector.huntBugs') }}
479
- </UButton>
452
+ <!-- Tracker entry points (import an issue, hunt bugs) live on the service frame's
453
+ header, where they are already scoped to a frame — the inspector doesn't
454
+ duplicate them. -->
480
455
  <UButton
481
456
  v-if="isContainer && documents.available && documents.anyConnected"
482
457
  color="neutral"
@@ -22,72 +22,64 @@ function addTask() {
22
22
  </script>
23
23
 
24
24
  <template>
25
+ <!-- Modules and tasks are two peer collapsible lists, not one "Contents" group: the
26
+ grouping added a level of nesting without adding information, and a service's task
27
+ list is long enough to want collapsing independently of its modules. -->
25
28
  <InspectorSection
26
- :title="t('inspector.container.title')"
27
- :hint="t('inspector.container.hint')"
28
- :count="tasks.length"
29
+ v-if="modules.length"
30
+ :title="t('inspector.container.modulesTitle')"
31
+ :hint="t('inspector.container.modulesHint')"
32
+ :count="modules.length"
29
33
  default-open
30
34
  >
31
- <!-- modules (services only) -->
32
- <div v-if="modules.length">
33
- <div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
34
- {{ t('inspector.container.modules', { count: modules.length }) }}
35
- </div>
36
- <ul class="space-y-1">
37
- <li
38
- v-for="m in modules"
39
- :key="m.id"
40
- class="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 hover:bg-slate-800/60"
41
- @click="ui.select(m.id)"
42
- >
43
- <UIcon name="i-lucide-package" class="h-3.5 w-3.5 text-violet-400" />
44
- <span class="truncate text-xs text-slate-200">{{ m.title }}</span>
45
- <span class="ms-auto text-[10px] text-slate-500">{{
46
- t(
47
- 'inspector.container.taskCount',
48
- { count: board.tasksOf(m.id).length },
49
- board.tasksOf(m.id).length,
50
- )
51
- }}</span>
52
- </li>
53
- </ul>
54
- </div>
35
+ <ul class="space-y-1">
36
+ <li
37
+ v-for="m in modules"
38
+ :key="m.id"
39
+ class="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 hover:bg-slate-800/60"
40
+ @click="ui.select(m.id)"
41
+ >
42
+ <UIcon name="i-lucide-package" class="h-3.5 w-3.5 text-violet-400" />
43
+ <span class="truncate text-xs text-slate-200">{{ m.title }}</span>
44
+ <span class="ms-auto text-[10px] text-slate-500">{{
45
+ t(
46
+ 'inspector.container.taskCount',
47
+ { count: board.tasksOf(m.id).length },
48
+ board.tasksOf(m.id).length,
49
+ )
50
+ }}</span>
51
+ </li>
52
+ </ul>
53
+ </InspectorSection>
55
54
 
56
- <div>
57
- <div class="mb-1 flex items-center justify-between">
58
- <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
59
- {{
60
- isFrame
61
- ? t('inspector.container.allTasks', { count: tasks.length })
62
- : t('inspector.container.tasks', { count: tasks.length })
63
- }}
64
- </span>
65
- <div class="flex items-center gap-1.5">
66
- <ReviewDebtBadge />
67
- <UButton size="xs" variant="soft" color="primary" icon="i-lucide-plus" @click="addTask">
68
- {{ t('inspector.container.addTask') }}
69
- </UButton>
70
- </div>
71
- </div>
72
- <ul v-if="tasks.length" class="space-y-1">
73
- <li
74
- v-for="task in tasks"
75
- :key="task.id"
76
- class="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 hover:bg-slate-800/60"
77
- @click="ui.select(task.id)"
78
- >
79
- <span
80
- class="h-2 w-2 shrink-0 rounded-full"
81
- :style="{ backgroundColor: STATUS_META[task.status].color }"
82
- />
83
- <span class="truncate text-xs text-slate-200">{{ task.title }}</span>
84
- <span class="ms-auto text-[10px] text-slate-500">{{
85
- STATUS_META[task.status].label
86
- }}</span>
87
- </li>
88
- </ul>
89
- <div v-else class="text-[11px] text-slate-500">{{ t('inspector.container.noTasks') }}</div>
90
- </div>
55
+ <InspectorSection
56
+ :title="isFrame ? t('inspector.container.allTasksTitle') : t('inspector.container.tasksTitle')"
57
+ :hint="t('inspector.container.tasksHint')"
58
+ :count="tasks.length"
59
+ default-open
60
+ >
61
+ <template #actions>
62
+ <ReviewDebtBadge />
63
+ <UButton size="xs" variant="soft" color="primary" icon="i-lucide-plus" @click="addTask">
64
+ {{ t('inspector.container.addTask') }}
65
+ </UButton>
66
+ </template>
67
+ <ul v-if="tasks.length" class="space-y-1">
68
+ <li
69
+ v-for="task in tasks"
70
+ :key="task.id"
71
+ class="flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 hover:bg-slate-800/60"
72
+ @click="ui.select(task.id)"
73
+ >
74
+ <span
75
+ class="h-2 w-2 shrink-0 rounded-full"
76
+ :style="{ backgroundColor: STATUS_META[task.status].color }"
77
+ />
78
+ <span class="truncate text-xs text-slate-200">{{ task.title }}</span>
79
+ <span class="ms-auto text-[10px] text-slate-500">{{ STATUS_META[task.status].label }}</span>
80
+ </li>
81
+ </ul>
82
+ <div v-else class="text-[11px] text-slate-500">{{ t('inspector.container.noTasks') }}</div>
91
83
  <p v-if="isFrame" class="text-[11px] text-slate-500">
92
84
  {{ t('inspector.container.servicesHint') }}
93
85
  </p>
@@ -2,6 +2,7 @@
2
2
  import { computed, reactive, ref, watch } from 'vue'
3
3
  import type { Block } from '~/types/domain'
4
4
  import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
5
+ import { showOverrideField } from '~/utils/uiMode'
5
6
 
6
7
  // Per-service (frame) post-release-health mapping: which observability monitors/SLOs the
7
8
  // `post-release-health` gate watches after this service's PRs ship. Keyed by THIS block's
@@ -11,6 +12,7 @@ const props = defineProps<{ block: Block }>()
11
12
 
12
13
  const store = useReleaseHealthStore()
13
14
  const ui = useUiStore()
15
+ const uiMode = useUiModeStore()
14
16
  const toast = useToast()
15
17
  const { t } = useI18n()
16
18
  const { confirmAction, toastDone } = useConfirmAction()
@@ -21,6 +23,14 @@ const draft = reactive({ monitorIds: '', sloIds: '', envTag: '' })
21
23
  const connected = computed(() => store.connection.connected)
22
24
  const saved = computed(() => store.configForBlock(props.block.id))
23
25
 
26
+ // Post-release health is an ADVANCED-tier concern: with no mapping the gate is a pass-through,
27
+ // so basic mode hides the whole section rather than showing a service-level knob whose absence
28
+ // changes nothing. `showOverrideField` (not a bare `isAdvanced`) because the mapping is per
29
+ // SERVICE and durable — once a teammate on the advanced tier maps monitors onto this frame,
30
+ // hiding the panel would leave a basic-mode user with releases being watched (and an on-call
31
+ // agent that can spawn) by a configuration they can neither see nor clear.
32
+ const show = computed(() => showOverrideField(uiMode.isAdvanced, saved.value))
33
+
24
34
  function parseIds(csv: string): string[] {
25
35
  return csv
26
36
  .split(',')
@@ -91,6 +101,7 @@ async function clear() {
91
101
 
92
102
  <template>
93
103
  <InspectorSection
104
+ v-if="show"
94
105
  :title="t('inspector.releaseHealth.title')"
95
106
  :hint="t('inspector.releaseHealth.sectionHint')"
96
107
  >
@@ -99,36 +99,71 @@ describe('navSlotFilter', () => {
99
99
  expect(kept).toContain('integrations-hub')
100
100
  expect(kept).toContain('workspace-settings')
101
101
  expect(kept).toContain('model-config')
102
- // ...and so does every destination that is the SOLE route to its capability, however deep
103
- // it feels: authoring a flow, the standards library, the PREnv/runner plumbing, and the
104
- // aggregate run-health / spend views.
102
+ // ...and so does everything the everyday delivery loop runs on, however deep it feels:
103
+ // authoring a flow, the standards library, and the PREnv/runner plumbing.
105
104
  expect(kept).toContain('build-pipeline')
106
105
  expect(kept).toContain('fragments')
107
106
  expect(kept).toContain('infrastructure')
108
107
  expect(kept).toContain('environment-setup')
109
- expect(kept).toContain('operator-dashboard')
110
- expect(kept).toContain('reports')
111
- // What drops is beside the delivery path (experimentation) or a shortcut into a surface
112
- // basic mode already reaches another way.
108
+ // What drops is either a shortcut basic mode reaches another way, or a capability
109
+ // deliberately kept out of the tier (experimentation, one-off repo setup, the
110
+ // deployment-wide operator rollups).
113
111
  expect(kept).not.toContain('sandbox')
114
112
  expect(kept).not.toContain('kaizen')
115
113
  expect(kept).not.toContain('merge-thresholds')
114
+ expect(kept).not.toContain('bootstrap-repo')
115
+ expect(kept).not.toContain('operator-dashboard')
116
+ expect(kept).not.toContain('reports')
116
117
  })
117
118
 
118
- it('never makes an advanced item the only route to its capability', () => {
119
- // The tier's governing rule (see the catalog comment): every advanced destination either
120
- // sits beside the delivery path, or is a shortcut into a surface a basic destination also
121
- // opens. Spelled out here as a table so promoting an item to `advanced` forces an explicit
122
- // claim about how basic mode still reaches it, rather than a silent capability loss.
123
- const ALTERNATIVE_ROUTE: Record<string, string> = {
124
- sandbox: 'none needed - experimentation surface, not on the delivery path',
125
- kaizen: 'none needed - self-grading history, not on the delivery path',
126
- 'merge-thresholds': 'workspace-settings -> Merge tab',
127
- 'service-fragment-defaults': 'workspace-settings -> Service best practices tab',
128
- 'local-models': 'integrations-hub -> Local runners',
119
+ it('states, per advanced item, whether basic mode still reaches its capability', () => {
120
+ // Marking an item `advanced` does one of two things, and which one has to be said out
121
+ // loud. `reached-another-way` promises a basic destination opens the same surface, so
122
+ // nothing is lost. `out-of-tier` concedes the opposite: the capability is ABSENT from
123
+ // basic mode and the tier switch is the only way to it. Both are legitimate; a silent
124
+ // one is not. The table is the claim, and it must match the catalog exactly, so adding
125
+ // `advanced: true` fails here until the reason is written down.
126
+ const REASON: Record<string, { kind: 'reached-another-way' | 'out-of-tier'; why: string }> = {
127
+ 'merge-thresholds': {
128
+ kind: 'reached-another-way',
129
+ why: 'workspace-settings -> Merge tab',
130
+ },
131
+ 'service-fragment-defaults': {
132
+ kind: 'reached-another-way',
133
+ why: 'workspace-settings -> Service best practices tab',
134
+ },
135
+ 'local-models': {
136
+ kind: 'reached-another-way',
137
+ why: 'integrations-hub -> Local runners',
138
+ },
139
+ sandbox: {
140
+ kind: 'out-of-tier',
141
+ why: 'experimentation surface, beside the delivery path',
142
+ },
143
+ kaizen: {
144
+ kind: 'out-of-tier',
145
+ why: 'self-grading history, beside the delivery path',
146
+ },
147
+ 'bootstrap-repo': {
148
+ kind: 'out-of-tier',
149
+ why: 'one-off new-repo setup, not part of running work on an existing board',
150
+ },
151
+ 'operator-dashboard': {
152
+ kind: 'out-of-tier',
153
+ why: 'deployment-wide health rollup - an operator job, not a delivery one',
154
+ },
155
+ reports: {
156
+ kind: 'out-of-tier',
157
+ why: 'deployment-wide spend/activity rollup - an operator job, not a delivery one',
158
+ },
129
159
  }
130
160
  const advanced = NAV_CONTRIBUTIONS.filter((i) => i.advanced).map((i) => i.id)
131
- expect(advanced.sort()).toEqual(Object.keys(ALTERNATIVE_ROUTE).sort())
161
+ expect(advanced.sort()).toEqual(Object.keys(REASON).sort())
162
+ // An `out-of-tier` item is a real capability loss, so the switcher back must survive basic
163
+ // mode — pinned by the next case. Assert here only that every claim carries a reason.
164
+ for (const [id, reason] of Object.entries(REASON)) {
165
+ expect(reason.why.length, `${id} has no stated reason`).toBeGreaterThan(0)
166
+ }
132
167
  })
133
168
 
134
169
  it('keeps the tier switch itself reachable in basic mode', () => {
@@ -153,24 +153,35 @@ const S = (...s: NavSurface[]) => s as readonly NavSurface[]
153
153
  * with accounts enabled).
154
154
  * - one icon per destination across shells.
155
155
  *
156
- * `advanced: true` marks the power-user half, hidden in basic interface mode. The line is
157
- * drawn by ROUTE COUNT, not by how advanced a surface feels: hiding the only way to reach a
158
- * capability removes the capability from the tier, while hiding a shortcut to a surface basic
159
- * mode already reaches removes nothing. So an item is advanced only when both of these hold —
160
- * it is not the sole route to its capability, and nothing on the delivery path needs it:
156
+ * `advanced: true` marks the power-user half, hidden in basic interface mode. Basic is the
157
+ * EVERYDAY DELIVERY SURFACE plan work on a board, run it, review and merge it so an item
158
+ * is advanced when it is not part of that loop. That happens two ways, and the difference
159
+ * matters when reading the list (it is the column `nav-contributions.spec.ts` makes you fill
160
+ * in):
161
161
  *
162
- * - `sandbox` / `kaizen`experimentation and self-grading surfaces, beside the delivery
163
- * path rather than on it.
162
+ * REACHED ANOTHER WAYhiding a shortcut removes nothing, because a basic destination opens
163
+ * the same surface:
164
164
  * - `merge-thresholds` / `service-fragment-defaults` — palette shortcuts into Workspace
165
165
  * settings tabs (Merge, Service best practices), which basic mode reaches via
166
166
  * `workspace-settings`.
167
167
  * - `local-models` — a per-user endpoint knob the Integrations hub already offers.
168
168
  *
169
- * Everything else stays in basic BECAUSE it is a sole route: authoring a flow
170
- * (`build-pipeline`), the standards/skills library (`fragments`, whose only other route is a
171
- * button two levels into Workspace settings), the PREnv + runner plumbing (`infrastructure`,
172
- * `environment-setup`), and the operator/cost views that are the only aggregate read of run
173
- * health and spend (`operator-dashboard`, `reports`).
169
+ * OUT OF THE TIER the sole route, hidden deliberately, so the capability itself is absent
170
+ * from basic mode. This is a product decision, not an oversight: each of these answers a
171
+ * question an everyday delivery user does not ask, and carrying it costs the basic nav more
172
+ * than the capability is worth there. Switching to advanced is the way to it.
173
+ * - `sandbox` / `kaizen` — experimentation and self-grading surfaces, beside the delivery
174
+ * path rather than on it.
175
+ * - `bootstrap-repo` — standing a NEW repository up from a reference architecture: a
176
+ * once-per-service setup act, not part of running work on an existing board.
177
+ * - `operator-dashboard` / `reports` — the deployment-wide health and spend rollups. They
178
+ * read across every workspace for whoever operates the deployment, which is a different
179
+ * job from delivering a task on one board.
180
+ *
181
+ * Everything else stays in basic because the delivery loop needs it: authoring a flow
182
+ * (`build-pipeline`), adding a repo (`add-from-repo`), the standards/skills library
183
+ * (`fragments`), the PREnv + runner plumbing (`infrastructure`, `environment-setup`), and the
184
+ * workspace/model configuration a run actually reads (`workspace-settings`, `model-config`).
174
185
  */
175
186
  export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
176
187
  {
@@ -210,6 +221,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
210
221
  labelKey: 'nav.bootstrapRepo',
211
222
  icon: 'i-lucide-git-branch-plus',
212
223
  surfaces: S('sidebar', 'command'),
224
+ advanced: true,
213
225
  gate: (g) => g.canManageIntegrations,
214
226
  action: 'bootstrapRepo',
215
227
  testId: 'nav-bootstrap-repo',
@@ -388,6 +400,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
388
400
  labelKey: 'nav.operatorDashboard',
389
401
  icon: 'i-lucide-gauge',
390
402
  surfaces: S('sidebar'),
403
+ advanced: true,
391
404
  gate: (g) => g.accountsEnabled && g.isAccountAdmin,
392
405
  action: 'operatorDashboard',
393
406
  testId: 'nav-operator-dashboard',
@@ -398,6 +411,7 @@ export const NAV_CONTRIBUTIONS: readonly NavContribution[] = [
398
411
  labelKey: 'nav.reports',
399
412
  icon: 'i-lucide-chart-column',
400
413
  surfaces: S('sidebar'),
414
+ advanced: true,
401
415
  gate: (g) => g.accountsEnabled && g.isAccountAdmin,
402
416
  action: 'reports',
403
417
  testId: 'nav-reports',
@@ -948,12 +948,12 @@
948
948
  "inspector": {
949
949
  "detectRepoUnresolved": "Das Repository dieses Blocks gehört nicht zu den verbundenen GitHub-Repos und kann daher nicht gelesen werden. Verbinde oder synchronisiere GitHub dafür und versuche es erneut.",
950
950
  "container": {
951
- "title": "Inhalte",
952
- "hint": "Die Module und Aufgaben innerhalb dieses Blocks. Klicke auf einen Eintrag, um ihn auf dem Board auszuwählen.",
953
- "modules": "Module ({count})",
951
+ "modulesTitle": "Module",
952
+ "modulesHint": "Die Module innerhalb dieses Service. Klicke auf ein Modul, um es auf dem Board auszuwählen.",
954
953
  "taskCount": "{count} Aufgabe | {count} Aufgaben",
955
- "allTasks": "Alle Aufgaben ({count})",
956
- "tasks": "Aufgaben ({count})",
954
+ "tasksTitle": "Aufgaben",
955
+ "allTasksTitle": "Alle Aufgaben",
956
+ "tasksHint": "Die Aufgaben innerhalb dieses Blocks. Klicke auf eine Aufgabe, um sie auf dem Board auszuwählen.",
957
957
  "addTask": "Aufgabe hinzufügen",
958
958
  "noTasks": "Noch keine Aufgaben. Füge eine hinzu, um mit der Arbeit zu beginnen.",
959
959
  "servicesHint": "Services sind langlebig und werden nicht abgeschlossen. Die Arbeit findet in ihren Aufgaben und Modulen statt."
@@ -1658,8 +1658,6 @@
1658
1658
  "lastIncorporatedRequirements": "Zuletzt eingearbeitete Anforderungen",
1659
1659
  "priorDocHint": "Aus der vorherigen Prüfung. Verwende es als Basis: bearbeite die Beschreibung oben und reiche erneut ein.",
1660
1660
  "bootstrapping": "Wird initialisiert…",
1661
- "importIssue": "Issue importieren",
1662
- "connectTracker": "Einen Tracker verbinden",
1663
1661
  "spawnFromDocument": "Aus Dokument erzeugen",
1664
1662
  "viewRequirements": "Anforderungen anzeigen",
1665
1663
  "reRun": "Erneut ausführen",
@@ -1670,8 +1668,7 @@
1670
1668
  "title": "Diesen Dienst archivieren?",
1671
1669
  "body": "„{name}“ und seine Aufgaben werden vom Board ausgeblendet. Du kannst den Dienst jederzeit wiederherstellen."
1672
1670
  },
1673
- "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}",
1674
- "huntBugs": "Fehler jagen"
1671
+ "runBlocked": "Blockiert durch eine unerledigte Abhängigkeit: {names} | Blockiert durch {count} unerledigte Abhängigkeiten: {names}"
1675
1672
  }
1676
1673
  },
1677
1674
  "layout": {
@@ -2446,6 +2443,7 @@
2446
2443
  "dragToResize": "Zum Ändern der Größe ziehen",
2447
2444
  "addFirstTask": "Die erste Aufgabe hinzufügen",
2448
2445
  "createInitiativeTitle": "Initiative erstellen",
2446
+ "huntBugsTitle": "Fehler jagen",
2449
2447
  "accessDenied": {
2450
2448
  "title": "Zugriff verweigert",
2451
2449
  "hint": "Sie haben keinen Zugriff auf das Repository, das diesem Service zugrunde liegt."
@@ -433,6 +433,7 @@
433
433
  "dragToResize": "Drag to resize",
434
434
  "addFirstTask": "Add the first task",
435
435
  "createInitiativeTitle": "Create initiative",
436
+ "huntBugsTitle": "Hunt bugs",
436
437
  "accessDenied": {
437
438
  "title": "Permission denied",
438
439
  "hint": "You don't have access to the repository backing this service."
@@ -694,12 +695,12 @@
694
695
  "inspector": {
695
696
  "detectRepoUnresolved": "This block's repository isn't among the connected GitHub repos, so it can't be read. Connect or sync GitHub for it, then retry.",
696
697
  "container": {
697
- "title": "Contents",
698
- "hint": "The modules and tasks inside this block. Click an entry to select it on the board.",
699
- "modules": "Modules ({count})",
698
+ "modulesTitle": "Modules",
699
+ "modulesHint": "The modules inside this service. Click one to select it on the board.",
700
700
  "taskCount": "{count} task | {count} tasks",
701
- "allTasks": "All tasks ({count})",
702
- "tasks": "Tasks ({count})",
701
+ "tasksTitle": "Tasks",
702
+ "allTasksTitle": "All tasks",
703
+ "tasksHint": "The tasks inside this block. Click one to select it on the board.",
703
704
  "addTask": "Add task",
704
705
  "noTasks": "No tasks yet. Add one to start work.",
705
706
  "servicesHint": "Services are long-lived and don't complete. Work happens in their tasks and modules."
@@ -1404,8 +1405,6 @@
1404
1405
  "lastIncorporatedRequirements": "Last incorporated requirements",
1405
1406
  "priorDocHint": "From the previous review. Use it as a base: edit the description above and resubmit.",
1406
1407
  "bootstrapping": "Bootstrapping…",
1407
- "importIssue": "Import issue",
1408
- "connectTracker": "Connect a tracker",
1409
1408
  "spawnFromDocument": "Spawn from document",
1410
1409
  "viewRequirements": "View Requirements",
1411
1410
  "reRun": "Re-run",
@@ -1419,8 +1418,7 @@
1419
1418
  "runBlocked": "Blocked by an unfinished dependency: {names} | Blocked by {count} unfinished dependencies: {names}",
1420
1419
  "@runBlocked": {
1421
1420
  "description": "Count-driven plural: {count} unfinished dependencies, {names} their comma-joined titles. Languages with more than two plural forms (e.g. Polish, Ukrainian: one/few/many) need the extra pipe-separated forms."
1422
- },
1423
- "huntBugs": "Hunt bugs"
1421
+ }
1424
1422
  }
1425
1423
  },
1426
1424
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Arrastra para cambiar el tamaño",
395
395
  "addFirstTask": "Añade la primera tarea",
396
396
  "createInitiativeTitle": "Crear iniciativa",
397
+ "huntBugsTitle": "Cazar errores",
397
398
  "accessDenied": {
398
399
  "title": "Permiso denegado",
399
400
  "hint": "No tienes acceso al repositorio que respalda este servicio."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "El repositorio de este bloque no está entre los repositorios de GitHub conectados, por lo que no se puede leer. Conecta o sincroniza GitHub para él y vuelve a intentarlo.",
633
634
  "container": {
634
- "title": "Contenido",
635
- "hint": "Los módulos y tareas dentro de este bloque. Haz clic en una entrada para seleccionarla en el tablero.",
636
- "modules": "Módulos ({count})",
635
+ "modulesTitle": "Módulos",
636
+ "modulesHint": "Los módulos dentro de este servicio. Haz clic en uno para seleccionarlo en el tablero.",
637
637
  "taskCount": "{count} tarea | {count} tareas",
638
- "allTasks": "Todas las tareas ({count})",
639
- "tasks": "Tareas ({count})",
638
+ "tasksTitle": "Tareas",
639
+ "allTasksTitle": "Todas las tareas",
640
+ "tasksHint": "Las tareas dentro de este bloque. Haz clic en una para seleccionarla en el tablero.",
640
641
  "addTask": "Añadir tarea",
641
642
  "noTasks": "Aún no hay tareas. Añade una para empezar a trabajar.",
642
643
  "servicesHint": "Los servicios son de larga duración y no se completan. El trabajo ocurre en sus tareas y módulos."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "Últimos requisitos incorporados",
1319
1320
  "priorDocHint": "De la revisión anterior. Úsalo como base: edita la descripción de arriba y reenvíala.",
1320
1321
  "bootstrapping": "Inicializando…",
1321
- "importIssue": "Importar incidencia",
1322
- "connectTracker": "Conectar un tracker",
1323
1322
  "spawnFromDocument": "Generar desde documento",
1324
1323
  "viewRequirements": "Ver requisitos",
1325
1324
  "reRun": "Reejecutar",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "¿Archivar este servicio?",
1354
1353
  "body": "«{name}» y sus tareas se ocultarán del tablero. Puedes restaurarlo en cualquier momento."
1355
1354
  },
1356
- "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}",
1357
- "huntBugs": "Cazar errores"
1355
+ "runBlocked": "Bloqueado por una dependencia sin terminar: {names} | Bloqueado por {count} dependencias sin terminar: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Glisser pour redimensionner",
395
395
  "addFirstTask": "Ajouter la première tâche",
396
396
  "createInitiativeTitle": "Creer une initiative",
397
+ "huntBugsTitle": "Chasser les bugs",
397
398
  "accessDenied": {
398
399
  "title": "Autorisation refusée",
399
400
  "hint": "Vous n'avez pas accès au dépôt sur lequel repose ce service."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "Le dépôt de ce bloc ne figure pas parmi les dépôts GitHub connectés et ne peut donc pas être lu. Connectez ou synchronisez GitHub pour ce dépôt, puis réessayez.",
633
634
  "container": {
634
- "title": "Contenu",
635
- "hint": "Les modules et les tâches contenus dans ce bloc. Cliquez sur une entrée pour la sélectionner sur le tableau.",
636
- "modules": "Modules ({count})",
635
+ "modulesTitle": "Modules",
636
+ "modulesHint": "Les modules de ce service. Cliquez sur l'un d'eux pour le sélectionner sur le tableau.",
637
637
  "taskCount": "{count} tâche | {count} tâches",
638
- "allTasks": "Toutes les tâches ({count})",
639
- "tasks": "Tâches ({count})",
638
+ "tasksTitle": "Tâches",
639
+ "allTasksTitle": "Toutes les tâches",
640
+ "tasksHint": "Les tâches contenues dans ce bloc. Cliquez sur l'une d'elles pour la sélectionner sur le tableau.",
640
641
  "addTask": "Ajouter une tâche",
641
642
  "noTasks": "Aucune tâche pour le moment. Ajoutez-en une pour commencer.",
642
643
  "servicesHint": "Les services sont permanents et ne se terminent pas. Le travail se fait dans leurs tâches et modules."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "Dernières exigences intégrées",
1319
1320
  "priorDocHint": "Issu de la revue précédente. Utilisez-le comme base : modifiez la description ci-dessus et resoumettez.",
1320
1321
  "bootstrapping": "Initialisation…",
1321
- "importIssue": "Importer une issue",
1322
- "connectTracker": "Connecter un tracker",
1323
1322
  "spawnFromDocument": "Créer à partir d'un document",
1324
1323
  "viewRequirements": "Voir les exigences",
1325
1324
  "reRun": "Relancer",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "Archiver ce service ?",
1354
1353
  "body": "« {name} » et ses tâches seront masqués du tableau. Vous pouvez le restaurer à tout moment."
1355
1354
  },
1356
- "runBlocked": "Bloqué par une dépendance non terminée : {names} | Bloqué par {count} dépendances non terminées : {names}",
1357
- "huntBugs": "Chasser les bugs"
1355
+ "runBlocked": "Bloqué par une dépendance non terminée : {names} | Bloqué par {count} dépendances non terminées : {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "גרור לשינוי גודל",
395
395
  "addFirstTask": "הוסף את המשימה הראשונה",
396
396
  "createInitiativeTitle": "יצירת יוזמה",
397
+ "huntBugsTitle": "צוד באגים",
397
398
  "accessDenied": {
398
399
  "title": "הגישה נדחתה",
399
400
  "hint": "אין לך גישה למאגר שעליו מבוסס שירות זה."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "מאגר הקוד של בלוק זה אינו נמצא בין מאגרי GitHub המחוברים, ולכן לא ניתן לקרוא אותו. חבר או סנכרן את GitHub עבורו ונסה שוב.",
633
634
  "container": {
634
- "title": "תוכן",
635
- "hint": "המודולים והמשימות בתוך בלוק זה. לחץ על פריט כדי לבחור אותו בלוח.",
636
- "modules": "מודולים ({count})",
635
+ "modulesTitle": "מודולים",
636
+ "modulesHint": "המודולים בתוך שירות זה. לחץ על מודול כדי לבחור אותו בלוח.",
637
637
  "taskCount": "משימה {count} | {count} משימות",
638
- "allTasks": "כל המשימות ({count})",
639
- "tasks": "משימות ({count})",
638
+ "tasksTitle": "משימות",
639
+ "allTasksTitle": "כל המשימות",
640
+ "tasksHint": "המשימות בתוך בלוק זה. לחץ על משימה כדי לבחור אותה בלוח.",
640
641
  "addTask": "הוסף משימה",
641
642
  "noTasks": "אין עדיין משימות. הוסף אחת כדי להתחיל בעבודה.",
642
643
  "servicesHint": "שירותים הם ארוכי טווח ואינם מסתיימים. העבודה מתבצעת במשימות ובמודולים שלהם."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "הדרישות האחרונות ששולבו",
1319
1320
  "priorDocHint": "מהסקירה הקודמת. השתמש בו כבסיס: ערוך את התיאור למעלה ושלח מחדש.",
1320
1321
  "bootstrapping": "מאתחל…",
1321
- "importIssue": "ייבא ניושן",
1322
- "connectTracker": "חבר מערכת מעקב",
1323
1322
  "spawnFromDocument": "צור ממסמך",
1324
1323
  "viewRequirements": "הצג דרישות",
1325
1324
  "reRun": "הרץ מחדש",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "להעביר שירות זה לארכיון?",
1354
1353
  "body": "\"{name}\" והמשימות שלו יוסתרו מהלוח. אפשר לשחזר אותו בכל עת."
1355
1354
  },
1356
- "runBlocked": "חסום על ידי תלות אחת שלא הושלמה: {names} | חסום על ידי {count} תלויות שלא הושלמו: {names}",
1357
- "huntBugs": "צוד באגים"
1355
+ "runBlocked": "חסום על ידי תלות אחת שלא הושלמה: {names} | חסום על ידי {count} תלויות שלא הושלמו: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -948,12 +948,12 @@
948
948
  "inspector": {
949
949
  "detectRepoUnresolved": "Il repository di questo blocco non e' tra i repository GitHub collegati, quindi non puo' essere letto. Collega o sincronizza GitHub per esso, poi riprova.",
950
950
  "container": {
951
- "title": "Contenuti",
952
- "hint": "I moduli e le attivita' all'interno di questo blocco. Fai clic su una voce per selezionarla sulla board.",
953
- "modules": "Moduli ({count})",
951
+ "modulesTitle": "Moduli",
952
+ "modulesHint": "I moduli all'interno di questo servizio. Fai clic su uno per selezionarlo sulla board.",
954
953
  "taskCount": "{count} attivita' | {count} attivita'",
955
- "allTasks": "Tutte le attivita' ({count})",
956
- "tasks": "Attivita' ({count})",
954
+ "tasksTitle": "Attivita'",
955
+ "allTasksTitle": "Tutte le attivita'",
956
+ "tasksHint": "Le attivita' all'interno di questo blocco. Fai clic su una per selezionarla sulla board.",
957
957
  "addTask": "Aggiungi attivita'",
958
958
  "noTasks": "Nessuna attivita' ancora. Aggiungine una per iniziare il lavoro.",
959
959
  "servicesHint": "I servizi sono di lunga durata e non si completano. Il lavoro avviene nelle loro attivita' e nei loro moduli."
@@ -1658,8 +1658,6 @@
1658
1658
  "lastIncorporatedRequirements": "Ultimi requisiti incorporati",
1659
1659
  "priorDocHint": "Dalla revisione precedente. Usalo come base: modifica la descrizione sopra e reinvia.",
1660
1660
  "bootstrapping": "Bootstrap in corso…",
1661
- "importIssue": "Importa issue",
1662
- "connectTracker": "Collega un tracker",
1663
1661
  "spawnFromDocument": "Genera da documento",
1664
1662
  "viewRequirements": "Visualizza requisiti",
1665
1663
  "reRun": "Riesegui",
@@ -1670,8 +1668,7 @@
1670
1668
  "title": "Archiviare questo servizio?",
1671
1669
  "body": "\"{name}\" e le sue attività verranno nascosti dalla board. Puoi ripristinarlo in qualsiasi momento."
1672
1670
  },
1673
- "runBlocked": "Bloccato da una dipendenza non completata: {names} | Bloccato da {count} dipendenze non completate: {names}",
1674
- "huntBugs": "Cerca bug"
1671
+ "runBlocked": "Bloccato da una dipendenza non completata: {names} | Bloccato da {count} dipendenze non completate: {names}"
1675
1672
  }
1676
1673
  },
1677
1674
  "layout": {
@@ -2446,6 +2443,7 @@
2446
2443
  "dragToResize": "Trascina per ridimensionare",
2447
2444
  "addFirstTask": "Aggiungi la prima attività",
2448
2445
  "createInitiativeTitle": "Crea iniziativa",
2446
+ "huntBugsTitle": "Cerca bug",
2449
2447
  "accessDenied": {
2450
2448
  "title": "Autorizzazione negata",
2451
2449
  "hint": "Non hai accesso al repository che supporta questo servizio."
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "ドラッグしてサイズ変更",
395
395
  "addFirstTask": "最初のタスクを追加",
396
396
  "createInitiativeTitle": "イニシアチブを作成",
397
+ "huntBugsTitle": "バグをハント",
397
398
  "accessDenied": {
398
399
  "title": "アクセスが拒否されました",
399
400
  "hint": "このサービスの基盤となるリポジトリにアクセスする権限がありません。"
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "このブロックのリポジトリは接続済みの GitHub リポジトリに含まれていないため、読み取れません。GitHub を接続または同期してから、もう一度お試しください。",
633
634
  "container": {
634
- "title": "内容",
635
- "hint": "このブロック内のモジュールとタスク。エントリをクリックするとボード上で選択されます。",
636
- "modules": "モジュール ({count})",
635
+ "modulesTitle": "モジュール",
636
+ "modulesHint": "このサービス内のモジュール。クリックするとボード上で選択されます。",
637
637
  "taskCount": "{count} 件のタスク | {count} 件のタスク",
638
- "allTasks": "すべてのタスク ({count})",
639
- "tasks": "タスク ({count})",
638
+ "tasksTitle": "タスク",
639
+ "allTasksTitle": "すべてのタスク",
640
+ "tasksHint": "このブロック内のタスク。クリックするとボード上で選択されます。",
640
641
  "addTask": "タスクを追加",
641
642
  "noTasks": "タスクはまだありません。追加して作業を始めましょう。",
642
643
  "servicesHint": "サービスは長期間存続し、完了しません。作業はそのタスクとモジュールで行われます。"
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "最後に取り込まれた要件",
1319
1320
  "priorDocHint": "前回のレビューより。これをベースに、上の説明を編集して再送信してください。",
1320
1321
  "bootstrapping": "ブートストラップ中…",
1321
- "importIssue": "Issue をインポート",
1322
- "connectTracker": "トラッカーを接続",
1323
1322
  "spawnFromDocument": "ドキュメントから生成",
1324
1323
  "viewRequirements": "要件を表示",
1325
1324
  "reRun": "再実行",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "このサービスをアーカイブしますか?",
1354
1353
  "body": "「{name}」とそのタスクはボードから非表示になります。いつでも復元できます。"
1355
1354
  },
1356
- "runBlocked": "未完了の依存関係によってブロックされています: {names} | {count} 件の未完了の依存関係によってブロックされています: {names}",
1357
- "huntBugs": "バグをハント"
1355
+ "runBlocked": "未完了の依存関係によってブロックされています: {names} | {count} 件の未完了の依存関係によってブロックされています: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Przeciągnij, aby zmienić rozmiar",
395
395
  "addFirstTask": "Dodaj pierwsze zadanie",
396
396
  "createInitiativeTitle": "Utworz inicjatywe",
397
+ "huntBugsTitle": "Poluj na błędy",
397
398
  "accessDenied": {
398
399
  "title": "Brak uprawnień",
399
400
  "hint": "Nie masz dostępu do repozytorium powiązanego z tą usługą."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "Repozytorium tego bloku nie znajduje się wśród połączonych repozytoriów GitHub, więc nie można go odczytać. Połącz lub zsynchronizuj GitHub dla tego repozytorium, a następnie spróbuj ponownie.",
633
634
  "container": {
634
- "title": "Zawartość",
635
- "hint": "Moduły i zadania wewnątrz tego bloku. Kliknij pozycję, aby zaznaczyć na tablicy.",
636
- "modules": "Moduły ({count})",
635
+ "modulesTitle": "Moduły",
636
+ "modulesHint": "Moduły wewnątrz tej usługi. Kliknij moduł, aby zaznaczyć go na tablicy.",
637
637
  "taskCount": "{count} zadanie | {count} zadania | {count} zadań",
638
- "allTasks": "Wszystkie zadania ({count})",
639
- "tasks": "Zadania ({count})",
638
+ "tasksTitle": "Zadania",
639
+ "allTasksTitle": "Wszystkie zadania",
640
+ "tasksHint": "Zadania wewnątrz tego bloku. Kliknij zadanie, aby zaznaczyć je na tablicy.",
640
641
  "addTask": "Dodaj zadanie",
641
642
  "noTasks": "Brak zadań. Dodaj jedno, aby rozpocząć pracę.",
642
643
  "servicesHint": "Usługi są długowieczne i nie kończą się. Praca odbywa się w ich zadaniach i modułach."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "Ostatnie włączone wymagania",
1319
1320
  "priorDocHint": "Z poprzedniej recenzji. Użyj go jako bazy: edytuj opis powyżej i prześlij ponownie.",
1320
1321
  "bootstrapping": "Inicjowanie…",
1321
- "importIssue": "Importuj zgłoszenie",
1322
- "connectTracker": "Połącz tracker",
1323
1322
  "spawnFromDocument": "Utwórz z dokumentu",
1324
1323
  "viewRequirements": "Wyświetl wymagania",
1325
1324
  "reRun": "Uruchom ponownie",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "Zarchiwizować tę usługę?",
1354
1353
  "body": "„{name}” i jej zadania zostaną ukryte z tablicy. Możesz przywrócić usługę w dowolnej chwili."
1355
1354
  },
1356
- "runBlocked": "Zablokowane przez niedokończoną zależność: {names} | Zablokowane przez {count} niedokończone zależności: {names} | Zablokowane przez {count} niedokończonych zależności: {names}",
1357
- "huntBugs": "Poluj na błędy"
1355
+ "runBlocked": "Zablokowane przez niedokończoną zależność: {names} | Zablokowane przez {count} niedokończone zależności: {names} | Zablokowane przez {count} niedokończonych zależności: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Yeniden boyutlandırmak için sürükle",
395
395
  "addFirstTask": "İlk görevi ekle",
396
396
  "createInitiativeTitle": "Girisim olustur",
397
+ "huntBugsTitle": "Hata avla",
397
398
  "accessDenied": {
398
399
  "title": "İzin reddedildi",
399
400
  "hint": "Bu hizmetin dayandığı depoya erişiminiz yok."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "Bu bloğun deposu bağlı GitHub depoları arasında değil, bu yüzden okunamıyor. Bunun için GitHub'ı bağlayın veya eşitleyin, ardından yeniden deneyin.",
633
634
  "container": {
634
- "title": "İçerik",
635
- "hint": "Bu bloğun içindeki modüller ve görevler. Bir öğeye tıklayarak panoda seçin.",
636
- "modules": "Modüller ({count})",
635
+ "modulesTitle": "Modüller",
636
+ "modulesHint": "Bu servisin içindeki modüller. Panoda seçmek için birine tıklayın.",
637
637
  "taskCount": "{count} görev | {count} görev",
638
- "allTasks": "Tüm görevler ({count})",
639
- "tasks": "Görevler ({count})",
638
+ "tasksTitle": "Görevler",
639
+ "allTasksTitle": "Tüm görevler",
640
+ "tasksHint": "Bu bloğun içindeki görevler. Panoda seçmek için birine tıklayın.",
640
641
  "addTask": "Görev ekle",
641
642
  "noTasks": "Henüz görev yok. Çalışmaya başlamak için bir tane ekleyin.",
642
643
  "servicesHint": "Servisler uzun ömürlüdür ve tamamlanmaz. Çalışma, görevlerinde ve modüllerinde gerçekleşir."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "Son dahil edilen gereksinimler",
1319
1320
  "priorDocHint": "Önceki incelemeden. Bir temel olarak kullanın: yukarıdaki açıklamayı düzenleyip yeniden gönderin.",
1320
1321
  "bootstrapping": "Önyükleniyor…",
1321
- "importIssue": "Sorunu içe aktar",
1322
- "connectTracker": "Bir izleyici bağla",
1323
1322
  "spawnFromDocument": "Belgeden oluştur",
1324
1323
  "viewRequirements": "Gereksinimleri Görüntüle",
1325
1324
  "reRun": "Yeniden çalıştır",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "Bu hizmet arşivlensin mi?",
1354
1353
  "body": "\"{name}\" ve görevleri panodan gizlenecek. İstediğin zaman geri yükleyebilirsin."
1355
1354
  },
1356
- "runBlocked": "Tamamlanmamış bir bağımlılık tarafından engellendi: {names} | Tamamlanmamış {count} bağımlılık tarafından engellendi: {names}",
1357
- "huntBugs": "Hata avla"
1355
+ "runBlocked": "Tamamlanmamış bir bağımlılık tarafından engellendi: {names} | Tamamlanmamış {count} bağımlılık tarafından engellendi: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
@@ -394,6 +394,7 @@
394
394
  "dragToResize": "Перетягніть, щоб змінити розмір",
395
395
  "addFirstTask": "Додати перше завдання",
396
396
  "createInitiativeTitle": "Створити ініціативу",
397
+ "huntBugsTitle": "Полювати на помилки",
397
398
  "accessDenied": {
398
399
  "title": "Доступ заборонено",
399
400
  "hint": "У вас немає доступу до репозиторію, що стоїть за цією службою."
@@ -631,12 +632,12 @@
631
632
  "inspector": {
632
633
  "detectRepoUnresolved": "Репозиторій цього блоку відсутній серед підключених репозиторіїв GitHub, тому його не вдається прочитати. Підключіть або синхронізуйте GitHub для нього та повторіть спробу.",
633
634
  "container": {
634
- "title": "Вміст",
635
- "hint": "Модулі та завдання всередині цього блока. Клацніть запис, щоб вибрати його на дошці.",
636
- "modules": "Модулі ({count})",
635
+ "modulesTitle": "Модулі",
636
+ "modulesHint": "Модулі всередині цього сервісу. Клацніть модуль, щоб вибрати його на дошці.",
637
637
  "taskCount": "{count} завдання | {count} завдання | {count} завдань",
638
- "allTasks": "Усі завдання ({count})",
639
- "tasks": "Завдання ({count})",
638
+ "tasksTitle": "Завдання",
639
+ "allTasksTitle": "Усі завдання",
640
+ "tasksHint": "Завдання всередині цього блока. Клацніть завдання, щоб вибрати його на дошці.",
640
641
  "addTask": "Додати завдання",
641
642
  "noTasks": "Завдань ще немає. Додайте одне, щоб почати роботу.",
642
643
  "servicesHint": "Сервіси довготривалі й не завершуються. Робота відбувається в їхніх завданнях і модулях."
@@ -1318,8 +1319,6 @@
1318
1319
  "lastIncorporatedRequirements": "Останні внесені вимоги",
1319
1320
  "priorDocHint": "З попереднього огляду. Використайте його як основу: відредагуйте опис вище та надішліть повторно.",
1320
1321
  "bootstrapping": "Ініціалізація…",
1321
- "importIssue": "Імпортувати задачу",
1322
- "connectTracker": "Підключити трекер",
1323
1322
  "spawnFromDocument": "Створити з документа",
1324
1323
  "viewRequirements": "Переглянути вимоги",
1325
1324
  "reRun": "Перезапустити",
@@ -1353,8 +1352,7 @@
1353
1352
  "title": "Заархівувати цей сервіс?",
1354
1353
  "body": "«{name}» та його завдання буде приховано з дошки. Ви можете відновити його будь-коли."
1355
1354
  },
1356
- "runBlocked": "Заблоковано незавершеною залежністю: {names} | Заблоковано {count} незавершеними залежностями: {names} | Заблоковано {count} незавершеними залежностями: {names}",
1357
- "huntBugs": "Полювати на помилки"
1355
+ "runBlocked": "Заблоковано незавершеною залежністю: {names} | Заблоковано {count} незавершеними залежностями: {names} | Заблоковано {count} незавершеними залежностями: {names}"
1358
1356
  }
1359
1357
  },
1360
1358
  "observability": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.174.0",
3
+ "version": "0.176.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",