@cat-factory/app 0.79.2 → 0.80.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.
Files changed (29) hide show
  1. package/app/components/documents/TaskContextDocs.vue +10 -7
  2. package/app/components/fragments/FragmentLibraryManager.vue +2 -10
  3. package/app/components/github/AddServiceFromRepoModal.vue +2 -1
  4. package/app/components/panels/InspectorPanel.vue +35 -18
  5. package/app/components/panels/inspector/ContainerSummary.vue +8 -2
  6. package/app/components/panels/inspector/EpicChildren.vue +9 -7
  7. package/app/components/panels/inspector/FrontendConfig.vue +175 -248
  8. package/app/components/panels/inspector/InspectorSection.vue +60 -0
  9. package/app/components/panels/inspector/ServiceConnections.vue +10 -10
  10. package/app/components/panels/inspector/ServiceFragments.vue +14 -9
  11. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +8 -7
  12. package/app/components/panels/inspector/ServiceTestConfig.vue +45 -57
  13. package/app/components/panels/inspector/TaskAgentConfig.vue +8 -5
  14. package/app/components/panels/inspector/TaskDependencies.vue +12 -8
  15. package/app/components/panels/inspector/TaskEstimateBadge.vue +9 -8
  16. package/app/components/panels/inspector/TaskExecution.vue +9 -2
  17. package/app/components/panels/inspector/TaskRunSettings.vue +13 -2
  18. package/app/components/panels/inspector/TaskStructure.vue +9 -2
  19. package/app/components/tasks/TaskContextIssues.vue +10 -7
  20. package/app/stores/fragmentLibrary.ts +4 -4
  21. package/i18n/locales/en.json +25 -3
  22. package/i18n/locales/es.json +25 -3
  23. package/i18n/locales/fr.json +25 -3
  24. package/i18n/locales/he.json +25 -3
  25. package/i18n/locales/ja.json +25 -3
  26. package/i18n/locales/pl.json +25 -3
  27. package/i18n/locales/tr.json +25 -3
  28. package/i18n/locales/uk.json +25 -3
  29. package/package.json +2 -2
@@ -1,12 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import type { Block } from '~/types/domain'
3
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
3
4
 
4
5
  // Service-level best-practice fragments (frame blocks). These are the programming
5
6
  // standards/guidelines for the whole service; at run time their bodies are folded
6
7
  // into the prompt of every `code-aware` agent on tasks under this service. Drawn from
7
8
  // the board's merged fragment catalog (built-in ∪ registered ∪ account ∪ workspace,
8
9
  // via the fragments store; static pool when the library is off), grouped by category.
9
- const props = defineProps<{ block: Block }>()
10
+ // `defaultOpen` expands the section on surfaces that embed this as the primary content
11
+ // (the add-service modal); the inspector leaves it collapsed.
12
+ const props = defineProps<{ block: Block; defaultOpen?: boolean }>()
10
13
 
11
14
  const board = useBoardStore()
12
15
  const fragments = useFragmentsStore()
@@ -76,11 +79,13 @@ function removeFragment(id: string) {
76
79
  </script>
77
80
 
78
81
  <template>
79
- <div>
80
- <div class="mb-1 flex items-center justify-between">
81
- <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
82
- {{ t('inspector.fragments.serviceTitle') }}
83
- </span>
82
+ <InspectorSection
83
+ :title="t('inspector.fragments.serviceTitle')"
84
+ :hint="t('inspector.fragments.serviceHint')"
85
+ :count="selectedFragments.length"
86
+ :default-open="props.defaultOpen"
87
+ >
88
+ <template #actions>
84
89
  <UDropdownMenu :items="fragmentMenu">
85
90
  <UButton
86
91
  size="xs"
@@ -90,8 +95,8 @@ function removeFragment(id: string) {
90
95
  trailing-icon="i-lucide-chevron-down"
91
96
  />
92
97
  </UDropdownMenu>
93
- </div>
94
- <div v-if="selectedFragments.length" class="mb-1 flex flex-wrap gap-1">
98
+ </template>
99
+ <div v-if="selectedFragments.length" class="flex flex-wrap gap-1">
95
100
  <UBadge
96
101
  v-for="f in selectedFragments"
97
102
  :key="f.id"
@@ -108,5 +113,5 @@ function removeFragment(id: string) {
108
113
  <div v-else class="text-[11px] text-slate-500">
109
114
  {{ t('inspector.fragments.serviceEmpty') }}
110
115
  </div>
111
- </div>
116
+ </InspectorSection>
112
117
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, reactive, ref, watch } from 'vue'
3
3
  import type { Block } from '~/types/domain'
4
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
4
5
 
5
6
  // Per-service (frame) post-release-health mapping: which observability monitors/SLOs the
6
7
  // `post-release-health` gate watches after this service's PRs ship. Keyed by THIS block's
@@ -89,11 +90,11 @@ async function clear() {
89
90
  </script>
90
91
 
91
92
  <template>
92
- <div class="space-y-2">
93
- <div class="flex items-center justify-between">
94
- <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
95
- {{ t('inspector.releaseHealth.title') }}
96
- </span>
93
+ <InspectorSection
94
+ :title="t('inspector.releaseHealth.title')"
95
+ :hint="t('inspector.releaseHealth.sectionHint')"
96
+ >
97
+ <template #actions>
97
98
  <UButton
98
99
  v-if="saved"
99
100
  color="error"
@@ -105,7 +106,7 @@ async function clear() {
105
106
  >
106
107
  {{ t('inspector.releaseHealth.clear') }}
107
108
  </UButton>
108
- </div>
109
+ </template>
109
110
 
110
111
  <!-- Disabled affordance until an observability integration is connected. -->
111
112
  <div
@@ -153,5 +154,5 @@ async function clear() {
153
154
  </UButton>
154
155
  </div>
155
156
  </div>
156
- </div>
157
+ </InspectorSection>
157
158
  </template>
@@ -17,6 +17,7 @@ import type {
17
17
  ProvisioningServiceDirCandidate,
18
18
  } from '@cat-factory/contracts'
19
19
  import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
20
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
20
21
 
21
22
  // Service-level (frame) configuration: the service-owned PROVISIONING — the provision
22
23
  // TYPE this service produces (`infraless` / `docker-compose` / `kubernetes` / `custom`)
@@ -30,6 +31,9 @@ const props = defineProps<{
30
31
  // Repo backing this service, supplied by the add-service modal when the block is
31
32
  // too fresh to be resolvable from the stores yet. Otherwise resolved below.
32
33
  repo?: { githubId: number; directory?: string | null }
34
+ // Expands the section on surfaces that embed this as the primary content (the
35
+ // add-service modal); the inspector leaves it collapsed.
36
+ defaultOpen?: boolean
33
37
  }>()
34
38
 
35
39
  const board = useBoardStore()
@@ -403,11 +407,11 @@ function setSize(value: InstanceSize) {
403
407
  </script>
404
408
 
405
409
  <template>
406
- <div class="space-y-3">
407
- <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
408
- {{ t('inspector.testConfig.title') }}
409
- </div>
410
-
410
+ <InspectorSection
411
+ :title="t('inspector.testConfig.title')"
412
+ :hint="t('inspector.testConfig.hint')"
413
+ :default-open="props.defaultOpen"
414
+ >
411
415
  <div class="space-y-1">
412
416
  <span class="text-[11px] text-slate-400">{{ t('inspector.testConfig.provisionType') }}</span>
413
417
  <div class="flex flex-wrap gap-1">
@@ -839,60 +843,44 @@ function setSize(value: InstanceSize) {
839
843
 
840
844
  <!-- Provisioning hints: advisory inputs to the ephemeral-environment provisioner.
841
845
  Collapsed by default — most services never tune them. -->
842
- <div class="border-t border-slate-800 pt-2">
843
- <button
844
- type="button"
845
- class="flex w-full items-center gap-1.5 text-start text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:text-slate-300"
846
- @click="showProvisioning = !showProvisioning"
847
- >
848
- <UIcon
849
- :name="showProvisioning ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
850
- class="h-3.5 w-3.5"
851
- />
852
- {{ t('inspector.testConfig.provisioningTitle') }}
853
- </button>
854
-
855
- <div v-if="showProvisioning" class="mt-2 space-y-3">
856
- <p class="text-[11px] leading-snug text-slate-500">
857
- {{ t('inspector.testConfig.provisioningHint') }}
858
- </p>
859
-
860
- <div class="space-y-1">
861
- <span class="text-[11px] text-slate-400">{{
862
- t('inspector.testConfig.cloudProvider')
863
- }}</span>
864
- <div class="flex flex-wrap gap-1">
865
- <UButton
866
- v-for="p in PROVIDERS"
867
- :key="p.value"
868
- :color="effectiveProvider === p.value ? 'primary' : 'neutral'"
869
- :variant="effectiveProvider === p.value ? 'soft' : 'ghost'"
870
- size="xs"
871
- @click="setProvider(p.value)"
872
- >
873
- {{ p.label }}
874
- </UButton>
875
- </div>
846
+ <InspectorSection
847
+ v-model:open="showProvisioning"
848
+ :title="t('inspector.testConfig.provisioningTitle')"
849
+ :hint="t('inspector.testConfig.provisioningHint')"
850
+ >
851
+ <div class="space-y-1">
852
+ <span class="text-[11px] text-slate-400">{{
853
+ t('inspector.testConfig.cloudProvider')
854
+ }}</span>
855
+ <div class="flex flex-wrap gap-1">
856
+ <UButton
857
+ v-for="p in PROVIDERS"
858
+ :key="p.value"
859
+ :color="effectiveProvider === p.value ? 'primary' : 'neutral'"
860
+ :variant="effectiveProvider === p.value ? 'soft' : 'ghost'"
861
+ size="xs"
862
+ @click="setProvider(p.value)"
863
+ >
864
+ {{ p.label }}
865
+ </UButton>
876
866
  </div>
867
+ </div>
877
868
 
878
- <div class="space-y-1">
879
- <span class="text-[11px] text-slate-400">{{
880
- t('inspector.testConfig.instanceSize')
881
- }}</span>
882
- <div class="flex flex-wrap gap-1">
883
- <UButton
884
- v-for="s in SIZES"
885
- :key="s.value"
886
- :color="(block.instanceSize ?? 'medium') === s.value ? 'primary' : 'neutral'"
887
- :variant="(block.instanceSize ?? 'medium') === s.value ? 'soft' : 'ghost'"
888
- size="xs"
889
- @click="setSize(s.value)"
890
- >
891
- {{ s.label }}
892
- </UButton>
893
- </div>
869
+ <div class="space-y-1">
870
+ <span class="text-[11px] text-slate-400">{{ t('inspector.testConfig.instanceSize') }}</span>
871
+ <div class="flex flex-wrap gap-1">
872
+ <UButton
873
+ v-for="s in SIZES"
874
+ :key="s.value"
875
+ :color="(block.instanceSize ?? 'medium') === s.value ? 'primary' : 'neutral'"
876
+ :variant="(block.instanceSize ?? 'medium') === s.value ? 'soft' : 'ghost'"
877
+ size="xs"
878
+ @click="setSize(s.value)"
879
+ >
880
+ {{ s.label }}
881
+ </UButton>
894
882
  </div>
895
883
  </div>
896
- </div>
897
- </div>
884
+ </InspectorSection>
885
+ </InspectorSection>
898
886
  </template>
@@ -3,6 +3,7 @@ import { computed } from 'vue'
3
3
  import type { Block } from '~/types/domain'
4
4
  import { useAgentConfigStore } from '~/stores/agentConfig'
5
5
  import { useExecutionStore } from '~/stores/execution'
6
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
6
7
 
7
8
  // Task-level configuration contributed by the agents in this task's selected
8
9
  // pipeline (e.g. the Playwright agent's e2e target: CI vs ephemeral). Each value is
@@ -49,10 +50,12 @@ function setValue(id: string, value: string) {
49
50
  </script>
50
51
 
51
52
  <template>
52
- <div v-if="descriptors.length" class="space-y-3">
53
- <div class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
54
- {{ t('inspector.agentConfig.title') }}
55
- </div>
53
+ <InspectorSection
54
+ v-if="descriptors.length"
55
+ :title="t('inspector.agentConfig.title')"
56
+ :hint="t('inspector.agentConfig.hint')"
57
+ :count="descriptors.length"
58
+ >
56
59
  <div v-for="d in descriptors" :key="d.id" class="space-y-1">
57
60
  <div class="flex items-center justify-between">
58
61
  <span class="text-[11px] text-slate-400">{{ d.label }}</span>
@@ -80,5 +83,5 @@ function setValue(id: string, value: string) {
80
83
  </div>
81
84
  <p class="text-[11px] leading-snug text-slate-500">{{ d.description }}</p>
82
85
  </div>
83
- </div>
86
+ </InspectorSection>
84
87
  </template>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Block } from '~/types/domain'
3
3
  import EmptyState from '~/components/common/EmptyState.vue'
4
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
4
5
 
5
6
  const props = defineProps<{ block: Block }>()
6
7
 
@@ -42,11 +43,14 @@ async function removeDep(dep: Block) {
42
43
  </script>
43
44
 
44
45
  <template>
45
- <div>
46
- <div class="mb-1 flex items-center justify-between">
47
- <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
48
- {{ t('inspector.dependencies.title') }}
49
- </span>
46
+ <!-- The warning flag keeps "this task is blocked" visible even while collapsed. -->
47
+ <InspectorSection
48
+ :title="t('inspector.dependencies.title')"
49
+ :hint="t('inspector.dependencies.hint')"
50
+ :count="deps.length"
51
+ :warning="!runnable"
52
+ >
53
+ <template #actions>
50
54
  <UDropdownMenu v-if="depMenu.length" :items="depMenu">
51
55
  <UButton
52
56
  size="xs"
@@ -56,7 +60,7 @@ async function removeDep(dep: Block) {
56
60
  trailing-icon="i-lucide-chevron-down"
57
61
  />
58
62
  </UDropdownMenu>
59
- </div>
63
+ </template>
60
64
  <div v-if="deps.length" class="flex flex-wrap gap-1">
61
65
  <UBadge
62
66
  v-for="d in deps"
@@ -82,8 +86,8 @@ async function removeDep(dep: Block) {
82
86
  icon="i-lucide-git-branch"
83
87
  :title="t('inspector.dependencies.empty')"
84
88
  />
85
- <div v-if="!runnable" class="mt-1 text-[10px] text-amber-400">
89
+ <div v-if="!runnable" class="text-[10px] text-amber-400">
86
90
  {{ t('inspector.dependencies.blocked') }}
87
91
  </div>
88
- </div>
92
+ </InspectorSection>
89
93
  </template>
@@ -4,6 +4,7 @@
4
4
  // used to gate consensus steps. Hidden when no estimate exists.
5
5
  import { computed } from 'vue'
6
6
  import type { Block } from '~/types/domain'
7
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
7
8
 
8
9
  const props = defineProps<{ block: Block }>()
9
10
  const { t, n } = useI18n()
@@ -28,13 +29,13 @@ function barClass(n: number): string {
28
29
  </script>
29
30
 
30
31
  <template>
31
- <section v-if="estimate" class="space-y-2">
32
- <div
33
- class="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-slate-400"
34
- >
35
- <UIcon name="i-lucide-gauge" class="h-3.5 w-3.5" />
36
- {{ t('inspector.estimate.title') }}
37
- </div>
32
+ <InspectorSection
33
+ v-if="estimate"
34
+ :title="t('inspector.estimate.title')"
35
+ :hint="t('inspector.estimate.hint')"
36
+ icon="i-lucide-gauge"
37
+ default-open
38
+ >
38
39
  <div class="space-y-1.5 rounded-lg border border-slate-800 bg-slate-900/40 p-2.5">
39
40
  <div v-for="axis in AXES" :key="axis.key" class="flex items-center gap-2">
40
41
  <span class="w-20 shrink-0 text-xs text-slate-400">{{ axis.label }}</span>
@@ -53,5 +54,5 @@ function barClass(n: number): string {
53
54
  {{ estimate.rationale }}
54
55
  </p>
55
56
  </div>
56
- </section>
57
+ </InspectorSection>
57
58
  </template>
@@ -10,6 +10,7 @@ import {
10
10
  import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
11
11
  import AgentFailureHistory from '~/components/board/AgentFailureHistory.vue'
12
12
  import EmptyState from '~/components/common/EmptyState.vue'
13
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
13
14
 
14
15
  const props = defineProps<{ block: Block }>()
15
16
 
@@ -185,7 +186,13 @@ async function mergePr() {
185
186
  </script>
186
187
 
187
188
  <template>
188
- <div class="space-y-4">
189
+ <!-- The live run surface stays open by default: it is what a user selecting a
190
+ running task is looking for (and what the e2e specs assert on). -->
191
+ <InspectorSection
192
+ :title="t('inspector.execution.title')"
193
+ :hint="t('inspector.execution.hint')"
194
+ default-open
195
+ >
189
196
  <!-- running pipeline -->
190
197
  <div v-if="instance">
191
198
  <div class="mb-1 flex items-center justify-between">
@@ -472,5 +479,5 @@ async function mergePr() {
472
479
  >
473
480
  {{ t('inspector.execution.mergePr') }}
474
481
  </UButton>
475
- </div>
482
+ </InspectorSection>
476
483
  </template>
@@ -5,6 +5,7 @@ import type { Block } from '~/types/domain'
5
5
  import type { WritebackOverride } from '~/types/tracker'
6
6
  import { mergePresetOptionLabel, mergePresetThresholds } from '~/utils/mergePreset'
7
7
  import { pipelineAllowedForFrame } from '~/utils/pipeline'
8
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
8
9
 
9
10
  const props = defineProps<{ block: Block }>()
10
11
 
@@ -256,7 +257,10 @@ const technicalLabel = computed(() => {
256
257
  </script>
257
258
 
258
259
  <template>
259
- <div class="space-y-4">
260
+ <InspectorSection
261
+ :title="t('inspector.runSettings.title')"
262
+ :hint="t('inspector.runSettings.hint')"
263
+ >
260
264
  <!-- pipeline -->
261
265
  <div>
262
266
  <div class="mb-1 flex items-center justify-between">
@@ -287,6 +291,9 @@ const technicalLabel = computed(() => {
287
291
  <div v-else class="text-[11px] text-slate-500">
288
292
  {{ t('inspector.runSettings.pipelineEmpty') }}
289
293
  </div>
294
+ <p class="mt-1 text-[11px] leading-snug text-slate-500">
295
+ {{ t('inspector.runSettings.pipelineHint') }}
296
+ </p>
290
297
  </div>
291
298
 
292
299
  <!-- merge policy preset -->
@@ -322,6 +329,9 @@ const technicalLabel = computed(() => {
322
329
  <div v-else class="text-[11px] text-slate-500">
323
330
  {{ t('inspector.runSettings.mergePresetEmpty') }}
324
331
  </div>
332
+ <p class="mt-1 text-[11px] leading-snug text-slate-500">
333
+ {{ t('inspector.runSettings.mergePolicyHint') }}
334
+ </p>
325
335
  </div>
326
336
 
327
337
  <!-- model preset -->
@@ -393,6 +403,7 @@ const technicalLabel = computed(() => {
393
403
  </div>
394
404
  </div>
395
405
  <p class="mt-1 text-[11px] text-slate-500">
406
+ {{ t('inspector.runSettings.modelPresetHint') }}
396
407
  {{ t('inspector.runSettings.modelPresetChangeHint') }}
397
408
  </p>
398
409
  </div>
@@ -558,5 +569,5 @@ const technicalLabel = computed(() => {
558
569
  {{ t('inspector.runSettings.autoStartHint') }}
559
570
  </div>
560
571
  </div>
561
- </div>
572
+ </InspectorSection>
562
573
  </template>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { Block } from '~/types/domain'
3
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
3
4
 
4
5
  const props = defineProps<{ block: Block }>()
5
6
 
@@ -76,7 +77,7 @@ function removeFragment(id: string) {
76
77
  </script>
77
78
 
78
79
  <template>
79
- <div class="space-y-4">
80
+ <InspectorSection :title="t('inspector.structure.title')" :hint="t('inspector.structure.hint')">
80
81
  <!-- module assignment -->
81
82
  <div>
82
83
  <div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
@@ -89,6 +90,9 @@ function removeFragment(id: string) {
89
90
  :placeholder="t('inspector.structure.modulePlaceholder')"
90
91
  icon="i-lucide-package"
91
92
  />
93
+ <p class="mt-1 text-[11px] leading-snug text-slate-500">
94
+ {{ t('inspector.structure.moduleHint') }}
95
+ </p>
92
96
  </div>
93
97
 
94
98
  <!-- best practices (prompt fragments) -->
@@ -124,6 +128,9 @@ function removeFragment(id: string) {
124
128
  <div v-else class="text-[11px] text-slate-500">
125
129
  {{ t('inspector.structure.bestPracticesEmpty') }}
126
130
  </div>
131
+ <p class="mt-1 text-[11px] leading-snug text-slate-500">
132
+ {{ t('inspector.structure.bestPracticesHint') }}
133
+ </p>
127
134
  </div>
128
- </div>
135
+ </InspectorSection>
129
136
  </template>
@@ -6,6 +6,7 @@
6
6
  // the structured nature of an issue is visible at a glance.
7
7
  import type { DropdownMenuItem } from '@nuxt/ui'
8
8
  import type { Block, TaskSourceKind } from '~/types/domain'
9
+ import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
9
10
 
10
11
  const props = defineProps<{ block: Block }>()
11
12
 
@@ -53,17 +54,19 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
53
54
  </script>
54
55
 
55
56
  <template>
56
- <div v-if="tasks.available" class="space-y-2">
57
- <div class="flex items-center justify-between">
58
- <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
59
- {{ t('tasks.contextIssues.title') }}
60
- </span>
57
+ <InspectorSection
58
+ v-if="tasks.available"
59
+ :title="t('tasks.contextIssues.title')"
60
+ :hint="t('tasks.contextIssues.hint')"
61
+ :count="linked.length"
62
+ >
63
+ <template #actions>
61
64
  <UDropdownMenu :items="attachMenu" :content="{ side: 'bottom', align: 'end' }">
62
65
  <UButton color="neutral" variant="soft" size="xs" icon="i-lucide-plus">{{
63
66
  t('tasks.contextIssues.attach')
64
67
  }}</UButton>
65
68
  </UDropdownMenu>
66
- </div>
69
+ </template>
67
70
 
68
71
  <div v-if="linked.length" class="space-y-1">
69
72
  <a
@@ -87,5 +90,5 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
87
90
  <p v-else class="text-[11px] text-slate-500">
88
91
  {{ t('tasks.contextIssues.emptyHint') }}
89
92
  </p>
90
- </div>
93
+ </InspectorSection>
91
94
  </template>
@@ -42,8 +42,8 @@ function fragmentLibrarySetup(kind: FragmentOwnerKind, resolveOwnerId: () => str
42
42
  const resolved = ref<ResolvedFragment[]>([])
43
43
  /** Linked guideline repos for this owner. */
44
44
  const sources = ref<FragmentSource[]>([])
45
- /** Per-source "changes available" counts from the last status check. */
46
- const sourceChanges = ref<Record<string, number>>({})
45
+ /** Per-source "changes available" flag from the last status check. */
46
+ const sourceChanges = ref<Record<string, boolean>>({})
47
47
  const loading = ref(false)
48
48
  /**
49
49
  * Account-tier document fragments only: the workspace whose stored
@@ -166,12 +166,12 @@ function fragmentLibrarySetup(kind: FragmentOwnerKind, resolveOwnerId: () => str
166
166
  }
167
167
  }
168
168
 
169
- /** Cheap "check for changes" for a source; caches the changed count. */
169
+ /** Lightweight commit-version "check for changes" for a source; caches the flag. */
170
170
  async function checkSource(sourceId: string) {
171
171
  const status = await api.fragmentSourceStatus(kind, requireOwnerId(), sourceId)
172
172
  sourceChanges.value = {
173
173
  ...sourceChanges.value,
174
- [sourceId]: status.changed ? status.changedCount : 0,
174
+ [sourceId]: status.changed,
175
175
  }
176
176
  return status
177
177
  }