@cat-factory/app 0.79.2 → 0.81.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 (30) 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/requirements/RequirementsReviewWindow.vue +126 -42
  20. package/app/components/tasks/TaskContextIssues.vue +10 -7
  21. package/app/stores/fragmentLibrary.ts +4 -4
  22. package/i18n/locales/en.json +28 -3
  23. package/i18n/locales/es.json +28 -3
  24. package/i18n/locales/fr.json +28 -3
  25. package/i18n/locales/he.json +28 -3
  26. package/i18n/locales/ja.json +28 -3
  27. package/i18n/locales/pl.json +28 -3
  28. package/i18n/locales/tr.json +28 -3
  29. package/i18n/locales/uk.json +28 -3
  30. package/package.json +2 -2
@@ -38,6 +38,9 @@ const reRequestNotes = ref<Record<string, string>>({})
38
38
  // Freeform "do it differently" comment when redoing a merge the human was unhappy with.
39
39
  const redoComment = ref('')
40
40
  const showRedo = ref(false)
41
+ // Human's explicit collapse choice for the whole incorporated-requirements section; null = follow
42
+ // the default (collapse it while there's still work to do so it doesn't dominate the window).
43
+ const docCollapsedOverride = ref<boolean | null>(null)
41
44
 
42
45
  // The seam contract (open/blockId/close + Escape handling + load-on-open) lives in
43
46
  // `useResultView`, so this window can't drift from the others. Declaring `onOpen` makes the
@@ -52,6 +55,7 @@ const { open, blockId, instanceId, stepIndex, close } = useResultView('requireme
52
55
  reRequestNotes.value = {}
53
56
  redoComment.value = ''
54
57
  showRedo.value = false
58
+ docCollapsedOverride.value = null
55
59
  void requirements.load(id)
56
60
  },
57
61
  })
@@ -256,6 +260,41 @@ const recommendationProgress = computed(() => {
256
260
  const ready = readyRecommendations.value.filter((r) => batchTimes.has(r.createdAt)).length
257
261
  return { ready, total: ready + generating.length }
258
262
  })
263
+ // Whether the human still has something to act on (findings to answer/dismiss or recommendations
264
+ // to decide). Drives the incorporated-document default collapse so the reference doc doesn't push
265
+ // the actionable findings/recommendations off-screen while there's still work.
266
+ const hasActionableWork = computed(() => {
267
+ if (!review.value) return false
268
+ const findingWork = review.value.items.some(
269
+ (i) => i.status === 'open' || i.status === 'answered' || i.status === 'recommend_requested',
270
+ )
271
+ return (
272
+ findingWork ||
273
+ readyRecommendations.value.length > 0 ||
274
+ generatingRecommendations.value.length > 0
275
+ )
276
+ })
277
+ // The whole incorporated-requirements section collapses as a unit (independent of the per-heading
278
+ // collapse below). Default: collapsed only in the pre-incorporation `ready`-style phase while
279
+ // there's still actionable work — so the (potentially long) reference doc stays out of the way of
280
+ // the findings the human is working through. In `merged` (inspect the draft to decide re-review vs
281
+ // redo) and `incorporated` (the settled deliverable) the document IS the thing to read, so it
282
+ // defaults expanded. The human's explicit toggle wins within a phase; a status change (below)
283
+ // clears it so a collapse from one phase doesn't leak into the next.
284
+ const docCollapsed = computed(
285
+ () =>
286
+ docCollapsedOverride.value ?? (!incorporated.value && !merged.value && hasActionableWork.value),
287
+ )
288
+ function toggleDoc() {
289
+ docCollapsedOverride.value = !docCollapsed.value
290
+ }
291
+ // Reset the manual collapse on every status transition so a collapse chosen in one phase doesn't
292
+ // persist into the next (e.g. a `ready` collapse leaking into `merged`, or surviving convergence to
293
+ // `incorporated` and hiding the final requirements) — each phase then falls back to its own default.
294
+ watch(status, () => {
295
+ docCollapsedOverride.value = null
296
+ })
297
+
259
298
  function isMarkedForRecommend(item: RequirementReviewItem): boolean {
260
299
  return markedForRecommend.value.has(item.id)
261
300
  }
@@ -751,9 +790,19 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
751
790
  </div>
752
791
  </section>
753
792
 
754
- <!-- incorporated document: the standard-format requirements -->
793
+ <!-- incorporated document: the standard-format requirements. The whole section
794
+ collapses as a unit (a long doc otherwise pushes the findings/recommendations
795
+ off-screen); the per-heading toggles below still work when it's expanded. -->
755
796
  <section v-if="outline" class="mt-6 border-t border-slate-800 pt-5">
756
- <div class="mb-3 flex items-center gap-1.5 text-[11px] text-emerald-400">
797
+ <button
798
+ class="mb-3 flex w-full items-center gap-1.5 text-[11px] text-emerald-400"
799
+ @click="toggleDoc"
800
+ >
801
+ <UIcon
802
+ name="i-lucide-chevron-right"
803
+ class="h-3.5 w-3.5 shrink-0 transition-transform"
804
+ :class="docCollapsed ? '' : 'rotate-90'"
805
+ />
757
806
  <UIcon name="i-lucide-file-check-2" class="h-3.5 w-3.5" />
758
807
  <span class="font-semibold uppercase tracking-wide">
759
808
  {{
@@ -762,29 +811,31 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
762
811
  : t('requirements.incorporatedDraft')
763
812
  }}
764
813
  </span>
765
- </div>
766
- <div v-for="s in outline.sections" :key="s.id" class="mb-2">
767
- <button
768
- v-if="s.title"
769
- class="group flex w-full items-center gap-2 text-start"
770
- @click="toggle(s.id)"
771
- >
772
- <UIcon
773
- name="i-lucide-chevron-right"
774
- class="h-3.5 w-3.5 shrink-0 text-slate-500 transition-transform"
775
- :class="collapsed[s.id] ? '' : 'rotate-90'"
776
- />
777
- <span
778
- class="font-semibold text-white"
779
- :class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
780
- v-html="s.titleHtml"
814
+ </button>
815
+ <div v-show="!docCollapsed">
816
+ <div v-for="s in outline.sections" :key="s.id" class="mb-2">
817
+ <button
818
+ v-if="s.title"
819
+ class="group flex w-full items-center gap-2 text-start"
820
+ @click="toggle(s.id)"
821
+ >
822
+ <UIcon
823
+ name="i-lucide-chevron-right"
824
+ class="h-3.5 w-3.5 shrink-0 text-slate-500 transition-transform"
825
+ :class="collapsed[s.id] ? '' : 'rotate-90'"
826
+ />
827
+ <span
828
+ class="font-semibold text-white"
829
+ :class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
830
+ v-html="s.titleHtml"
831
+ />
832
+ </button>
833
+ <div
834
+ v-show="!s.title || !collapsed[s.id]"
835
+ class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
836
+ v-html="s.bodyHtml"
781
837
  />
782
- </button>
783
- <div
784
- v-show="!s.title || !collapsed[s.id]"
785
- class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
786
- v-html="s.bodyHtml"
787
- />
838
+ </div>
788
839
  </div>
789
840
  </section>
790
841
  </template>
@@ -806,12 +857,63 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
806
857
  <span>{{ t('requirements.stats.answered') }}</span>
807
858
  <span class="text-slate-300">{{ answeredCount }}</span>
808
859
  </div>
860
+ <!-- awaited recommendations — kept here (always visible) so the human can see what
861
+ the Writer is still producing / what's waiting on them even while reading the
862
+ incorporated document or acting elsewhere in the window. -->
863
+ <template v-if="generatingRecommendations.length || readyRecommendations.length">
864
+ <div
865
+ class="flex items-center gap-1.5 border-t border-slate-800/60 pt-2 text-indigo-300"
866
+ >
867
+ <UIcon name="i-lucide-wand-2" class="h-3 w-3" />
868
+ <span class="font-medium">{{ t('requirements.stats.recommendations') }}</span>
869
+ </div>
870
+ <div
871
+ v-if="generatingRecommendations.length"
872
+ class="flex items-center justify-between"
873
+ >
874
+ <span>{{ t('requirements.stats.recsGenerating') }}</span>
875
+ <span class="text-indigo-300">{{ generatingRecommendations.length }}</span>
876
+ </div>
877
+ <div v-if="readyRecommendations.length" class="flex items-center justify-between">
878
+ <span>{{ t('requirements.stats.recsToReview') }}</span>
879
+ <span class="text-indigo-300">{{ readyRecommendations.length }}</span>
880
+ </div>
881
+ </template>
809
882
  <div v-if="review.model" class="flex items-center justify-between">
810
883
  <span>{{ t('requirements.stats.model') }}</span>
811
884
  <span class="truncate ps-2 text-slate-500">{{ review.model }}</span>
812
885
  </div>
813
886
  </div>
814
887
 
888
+ <!-- Request the Requirement Writer for the marked findings. Kept OUT of the
889
+ status-scoped blocks below so it's available whenever the review is still
890
+ editable — the `ready` first pass AND a `merged` review being reworked — not
891
+ only when status is exactly `ready`. Scoped to exactly those two states (NOT a
892
+ bare `!frozen`, which would also expose it in `exceeded`, where the run is parked
893
+ on the cap decision and a fresh recommendation batch has no path to settle). -->
894
+ <div
895
+ v-if="review && markedForRecommend.size > 0 && (status === 'ready' || merged)"
896
+ class="border-t border-slate-800 pt-4"
897
+ >
898
+ <UButton
899
+ color="primary"
900
+ variant="soft"
901
+ size="sm"
902
+ block
903
+ icon="i-lucide-wand-2"
904
+ :loading="recommending"
905
+ @click="requestRecommendations"
906
+ >
907
+ {{
908
+ t(
909
+ 'requirements.actions.requestRecommendations',
910
+ { count: markedForRecommend.size },
911
+ markedForRecommend.size,
912
+ )
913
+ }}
914
+ </UButton>
915
+ </div>
916
+
815
917
  <!-- action: ready (answer → incorporate / proceed) -->
816
918
  <div
817
919
  v-if="review && status === 'ready'"
@@ -841,24 +943,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
841
943
  >
842
944
  {{ t('requirements.actions.incorporateAnswers') }}
843
945
  </UButton>
844
- <UButton
845
- v-if="markedForRecommend.size > 0"
846
- color="primary"
847
- variant="soft"
848
- size="sm"
849
- block
850
- icon="i-lucide-wand-2"
851
- :loading="recommending"
852
- @click="requestRecommendations"
853
- >
854
- {{
855
- t(
856
- 'requirements.actions.requestRecommendations',
857
- { count: markedForRecommend.size },
858
- markedForRecommend.size,
859
- )
860
- }}
861
- </UButton>
862
946
  <p class="text-[11px] leading-relaxed text-slate-500">
863
947
  <template v-if="canProceed">
864
948
  {{ t('requirements.help.canProceed') }}
@@ -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
  }
@@ -430,6 +430,8 @@
430
430
  },
431
431
  "inspector": {
432
432
  "container": {
433
+ "title": "Contents",
434
+ "hint": "The modules and tasks inside this block. Click an entry to select it on the board.",
433
435
  "modules": "Modules ({count})",
434
436
  "taskCount": "{count} task | {count} tasks",
435
437
  "allTasks": "All tasks ({count})",
@@ -440,6 +442,7 @@
440
442
  },
441
443
  "epicChildren": {
442
444
  "title": "Epic tasks",
445
+ "hint": "All tasks assigned to this epic, grouped by the service and module they live in. An epic only groups tasks; it does not run anything itself.",
443
446
  "doneCount": "{done}/{total} done",
444
447
  "empty": "No tasks belong to this epic yet. Import an epic's children, or set a task's epic.",
445
448
  "unassigned": "Unassigned"
@@ -478,6 +481,7 @@
478
481
  },
479
482
  "fragments": {
480
483
  "serviceTitle": "Service best practices",
484
+ "serviceHint": "Programming standards for the whole service. The content of each selected fragment is added to the prompt of every code-aware agent working on tasks under this service.",
481
485
  "serviceEmpty": "None. Code-aware agents on this service follow their default guidance.",
482
486
  "manageBoard": "Manage this board's fragment library…",
483
487
  "manageAccount": "Manage account fragments…"
@@ -562,6 +566,7 @@
562
566
  },
563
567
  "releaseHealth": {
564
568
  "title": "Post-release health",
569
+ "sectionHint": "After this service's changes merge and ship, a gate watches the mapped monitors and SLOs for a while. On a regression it starts an on-call agent to investigate; nothing is reverted automatically.",
565
570
  "clear": "Clear",
566
571
  "connectPrompt": "Connect an observability integration to watch this service after releases.",
567
572
  "connectFirst": "Connect an observability integration first",
@@ -578,6 +583,7 @@
578
583
  },
579
584
  "testConfig": {
580
585
  "title": "Test infrastructure",
586
+ "hint": "How a test environment is stood up for this service when a pipeline needs to run it: no infrastructure, a Docker Compose file, Kubernetes manifests, or a custom manifest type.",
581
587
  "provisionType": "Provision type",
582
588
  "provisionTypeHint": "How this service stands up its environment for the Tester. The workspace configures how each type is handled (the engine + connection).",
583
589
  "provisionTypes": {
@@ -661,11 +667,13 @@
661
667
  },
662
668
  "agentConfig": {
663
669
  "title": "Agent configuration",
670
+ "hint": "Options contributed by the agents in this task's pipeline. Each option stays editable until its agent starts, then it is locked for that run.",
664
671
  "inherited": "inherited from service",
665
672
  "frozen": "Frozen: the agent has started"
666
673
  },
667
674
  "dependencies": {
668
675
  "title": "Depends on",
676
+ "hint": "Tasks that must merge before this one can run. The Run control stays locked until every dependency is done.",
669
677
  "merged": "Merged",
670
678
  "notMerged": "Not merged yet",
671
679
  "empty": "No dependencies. Can run any time.",
@@ -677,11 +685,14 @@
677
685
  },
678
686
  "estimate": {
679
687
  "title": "Estimate",
688
+ "hint": "Produced by the task estimator agent: how complex, risky, and impactful this task looks. Used to decide whether consensus (multi-run) steps are worth running.",
680
689
  "complexity": "Complexity",
681
690
  "risk": "Risk",
682
691
  "impact": "Impact"
683
692
  },
684
693
  "execution": {
694
+ "title": "Execution",
695
+ "hint": "The live run of this task's pipeline: each agent step, its progress, and the resulting pull request.",
685
696
  "stage": {
686
697
  "incorporating": "Incorporating…",
687
698
  "reviewing": "Re-reviewing…",
@@ -735,12 +746,18 @@
735
746
  "mergePr": "Merge PR"
736
747
  },
737
748
  "structure": {
749
+ "title": "Structure",
750
+ "hint": "Where this task lives in its service and which coding standards its agents follow.",
738
751
  "module": "Module",
739
752
  "modulePlaceholder": "e.g. Sessions (created on implement if new)",
753
+ "moduleHint": "The service module this task belongs to. Type a name; a module that does not exist yet is created when the task is implemented.",
740
754
  "bestPractices": "Best practices",
741
- "bestPracticesEmpty": "None. Agents follow their default guidance."
755
+ "bestPracticesEmpty": "None. Agents follow their default guidance.",
756
+ "bestPracticesHint": "Best-practice fragments folded into the prompts of this task's agents, on top of the service-level standards."
742
757
  },
743
758
  "runSettings": {
759
+ "title": "Run settings",
760
+ "hint": "How this task's pipeline runs: which pipeline starts, which merge and model policies apply, and what happens around the run.",
744
761
  "unassigned": "Unassigned",
745
762
  "workspaceDefault": "Workspace default",
746
763
  "workspaceDefaultParen": "(workspace default)",
@@ -759,10 +776,13 @@
759
776
  },
760
777
  "pipeline": "Pipeline",
761
778
  "pipelineEmpty": "No default. Pick a pipeline when you run this task.",
779
+ "pipelineHint": "The pipeline the Run button starts by default: the sequence of agent steps (for example spec, code, test, merge) executed for this task.",
762
780
  "mergePolicy": "Merge policy",
763
781
  "mergePresetDetail": "{name}: auto-merge when complexity ≤ {complexity}, risk ≤ {risk}, impact ≤ {impact}; up to {attempts} CI-fix attempts.",
764
782
  "mergePresetEmpty": "No preset configured. The merger raises a review notification for every PR.",
783
+ "mergePolicyHint": "Decides when a finished pull request merges automatically instead of waiting for a human review, and how many CI-fix attempts a run gets.",
765
784
  "modelPreset": "Model preset",
785
+ "modelPresetHint": "Decides which AI model each agent step runs on.",
766
786
  "modelPresetBase": "base {model}",
767
787
  "modelPresetOverrides": ", {count} override | , {count} overrides",
768
788
  "modelPresetEmpty": "No preset configured. Agents run on the deployment's default routing.",
@@ -2676,6 +2696,7 @@
2676
2696
  },
2677
2697
  "taskDocs": {
2678
2698
  "heading": "Context documents",
2699
+ "hint": "Documents attached to this task as context. Their content is given to the agents that work on the task.",
2679
2700
  "attach": "Attach",
2680
2701
  "importPage": "Import a page…",
2681
2702
  "empty": "Attach a requirement, RFC or PRD so agents see it while implementing this task.",
@@ -2696,6 +2717,7 @@
2696
2717
  },
2697
2718
  "contextIssues": {
2698
2719
  "title": "Context issues",
2720
+ "hint": "Tracker issues attached to this task as context. Their content is given to the agents that work on the task.",
2699
2721
  "attach": "Attach",
2700
2722
  "attached": "Issue attached",
2701
2723
  "attachFailed": "Could not attach",
@@ -3056,6 +3078,9 @@
3056
3078
  "findings": "Findings",
3057
3079
  "open": "Open",
3058
3080
  "answered": "Answered",
3081
+ "recommendations": "Recommendations",
3082
+ "recsGenerating": "Generating",
3083
+ "recsToReview": "To review",
3059
3084
  "model": "Model"
3060
3085
  },
3061
3086
  "actions": {
@@ -3802,7 +3827,7 @@
3802
3827
  "sources": {
3803
3828
  "metaSynced": "synced · ref {ref}",
3804
3829
  "metaNever": "never synced · ref {ref}",
3805
- "changes": "{count} change | {count} changes",
3830
+ "changes": "Changes",
3806
3831
  "empty": "No linked guideline repos. Link one below to import its Markdown files as fragments.",
3807
3832
  "linkTitle": "Link a guideline repo",
3808
3833
  "ownerPlaceholder": "owner",
@@ -3827,7 +3852,7 @@
3827
3852
  "linkSourceFailed": "Could not link source",
3828
3853
  "synced": "Synced: {updated} updated, {removed} removed",
3829
3854
  "syncFailed": "Could not sync source",
3830
- "changesAvailable": "{count} change available | {count} changes available",
3855
+ "changesAvailable": "Changes available",
3831
3856
  "upToDate": "Up to date",
3832
3857
  "checkSourceFailed": "Could not check source",
3833
3858
  "sourceUnlinked": "Source unlinked",
@@ -387,6 +387,8 @@
387
387
  },
388
388
  "inspector": {
389
389
  "container": {
390
+ "title": "Contenido",
391
+ "hint": "Los módulos y tareas dentro de este bloque. Haz clic en una entrada para seleccionarla en el tablero.",
390
392
  "modules": "Módulos ({count})",
391
393
  "taskCount": "{count} tarea | {count} tareas",
392
394
  "allTasks": "Todas las tareas ({count})",
@@ -397,6 +399,7 @@
397
399
  },
398
400
  "epicChildren": {
399
401
  "title": "Tareas de la épica",
402
+ "hint": "Todas las tareas asignadas a esta épica, agrupadas por el servicio y el módulo donde viven. Una épica solo agrupa tareas; no ejecuta nada por sí misma.",
400
403
  "doneCount": "{done}/{total} completadas",
401
404
  "empty": "Aún no hay tareas en esta épica. Importa las hijas de una épica o asigna la épica de una tarea.",
402
405
  "unassigned": "Sin asignar"
@@ -435,6 +438,7 @@
435
438
  },
436
439
  "fragments": {
437
440
  "serviceTitle": "Buenas prácticas del servicio",
441
+ "serviceHint": "Estándares de programación para todo el servicio. El contenido de cada fragmento seleccionado se añade al prompt de cada agente que conoce el código y trabaja en tareas de este servicio.",
438
442
  "serviceEmpty": "Ninguna. Los agentes que conocen el código en este servicio siguen su guía predeterminada.",
439
443
  "manageBoard": "Gestionar la biblioteca de fragmentos de este tablero…",
440
444
  "manageAccount": "Gestionar los fragmentos de la cuenta…"
@@ -519,6 +523,7 @@
519
523
  },
520
524
  "releaseHealth": {
521
525
  "title": "Salud posterior al lanzamiento",
526
+ "sectionHint": "Cuando los cambios de este servicio se fusionan y publican, una verificación vigila durante un tiempo los monitores y SLO configurados. Ante una regresión inicia un agente de guardia para investigar; nada se revierte automáticamente.",
522
527
  "clear": "Limpiar",
523
528
  "connectPrompt": "Conecta una integración de observabilidad para vigilar este servicio tras los lanzamientos.",
524
529
  "connectFirst": "Conecta primero una integración de observabilidad",
@@ -535,6 +540,7 @@
535
540
  },
536
541
  "testConfig": {
537
542
  "title": "Infraestructura de pruebas",
543
+ "hint": "Cómo se levanta un entorno de prueba para este servicio cuando un pipeline necesita ejecutarlo: sin infraestructura, un archivo de Docker Compose, manifiestos de Kubernetes o un tipo de manifiesto personalizado.",
538
544
  "provisionType": "Tipo de aprovisionamiento",
539
545
  "provisionTypeHint": "Cómo este servicio levanta su entorno para el Tester. El espacio de trabajo configura cómo se gestiona cada tipo (el motor + la conexión).",
540
546
  "provisionTypes": {
@@ -618,11 +624,13 @@
618
624
  },
619
625
  "agentConfig": {
620
626
  "title": "Configuración del agente",
627
+ "hint": "Opciones aportadas por los agentes del pipeline de esta tarea. Cada opción se puede editar hasta que su agente arranca; después queda bloqueada para esa ejecución.",
621
628
  "inherited": "heredado del servicio",
622
629
  "frozen": "Congelado: el agente ya ha comenzado"
623
630
  },
624
631
  "dependencies": {
625
632
  "title": "Depende de",
633
+ "hint": "Tareas que deben fusionarse antes de que esta pueda ejecutarse. El control Ejecutar permanece bloqueado hasta que todas las dependencias estén completas.",
626
634
  "merged": "Fusionado",
627
635
  "notMerged": "Aún sin fusionar",
628
636
  "empty": "Sin dependencias. Puede ejecutarse en cualquier momento.",
@@ -634,11 +642,14 @@
634
642
  },
635
643
  "estimate": {
636
644
  "title": "Estimación",
645
+ "hint": "Producida por el agente estimador de tareas: cuán compleja, arriesgada e impactante parece esta tarea. Se usa para decidir si merecen la pena los pasos de consenso (multiejecución).",
637
646
  "complexity": "Complejidad",
638
647
  "risk": "Riesgo",
639
648
  "impact": "Impacto"
640
649
  },
641
650
  "execution": {
651
+ "title": "Ejecución",
652
+ "hint": "La ejecución en vivo del pipeline de esta tarea: cada paso de agente, su progreso y el pull request resultante.",
642
653
  "stage": {
643
654
  "incorporating": "Incorporando…",
644
655
  "reviewing": "Revisando de nuevo…",
@@ -692,12 +703,18 @@
692
703
  }
693
704
  },
694
705
  "structure": {
706
+ "title": "Estructura",
707
+ "hint": "Dónde vive esta tarea dentro de su servicio y qué estándares de código siguen sus agentes.",
695
708
  "module": "Módulo",
696
709
  "modulePlaceholder": "p. ej. Sesiones (creado al implementar si es nuevo)",
710
+ "moduleHint": "El módulo del servicio al que pertenece esta tarea. Escribe un nombre; un módulo que aún no existe se crea cuando la tarea se implementa.",
697
711
  "bestPractices": "Buenas prácticas",
698
- "bestPracticesEmpty": "Ninguna. Los agentes siguen su guía predeterminada."
712
+ "bestPracticesEmpty": "Ninguna. Los agentes siguen su guía predeterminada.",
713
+ "bestPracticesHint": "Fragmentos de buenas prácticas incorporados a los prompts de los agentes de esta tarea, además de los estándares a nivel de servicio."
699
714
  },
700
715
  "runSettings": {
716
+ "title": "Ajustes de ejecución",
717
+ "hint": "Cómo se ejecuta el pipeline de esta tarea: qué pipeline arranca, qué políticas de fusión y de modelo se aplican y qué ocurre alrededor de la ejecución.",
701
718
  "unassigned": "Sin asignar",
702
719
  "workspaceDefault": "Predeterminado del espacio de trabajo",
703
720
  "workspaceDefaultParen": "(predeterminado del espacio de trabajo)",
@@ -716,10 +733,13 @@
716
733
  },
717
734
  "pipeline": "Pipeline",
718
735
  "pipelineEmpty": "Sin predeterminado. Elige un pipeline cuando ejecutes esta tarea.",
736
+ "pipelineHint": "El pipeline que el botón Ejecutar inicia por defecto: la secuencia de pasos de agente (por ejemplo especificación, código, pruebas, fusión) que se ejecuta para esta tarea.",
719
737
  "mergePolicy": "Política de fusión",
720
738
  "mergePresetDetail": "{name}: fusión automática cuando complejidad ≤ {complexity}, riesgo ≤ {risk}, impacto ≤ {impact}; hasta {attempts} intentos de corrección de CI.",
721
739
  "mergePresetEmpty": "No hay preajuste configurado. El fusionador genera una notificación de revisión para cada PR.",
740
+ "mergePolicyHint": "Decide cuándo un pull request terminado se fusiona automáticamente en lugar de esperar una revisión humana, y cuántos intentos de arreglo de CI tiene una ejecución.",
722
741
  "modelPreset": "Preajuste de modelo",
742
+ "modelPresetHint": "Decide con qué modelo de IA se ejecuta cada paso de agente.",
723
743
  "modelPresetBase": "base {model}",
724
744
  "modelPresetOverrides": ", {count} anulación | , {count} anulaciones",
725
745
  "modelPresetEmpty": "No hay preajuste configurado. Los agentes se ejecutan con el enrutamiento predeterminado del despliegue.",
@@ -2602,6 +2622,7 @@
2602
2622
  },
2603
2623
  "taskDocs": {
2604
2624
  "heading": "Documentos de contexto",
2625
+ "hint": "Documentos adjuntos a esta tarea como contexto. Su contenido se entrega a los agentes que trabajan en la tarea.",
2605
2626
  "attach": "Adjuntar",
2606
2627
  "importPage": "Importar una página…",
2607
2628
  "empty": "Adjunta un requisito, RFC o PRD para que los agentes lo vean mientras implementan esta tarea.",
@@ -2622,6 +2643,7 @@
2622
2643
  },
2623
2644
  "contextIssues": {
2624
2645
  "title": "Incidencias de contexto",
2646
+ "hint": "Incidencias del tracker adjuntas a esta tarea como contexto. Su contenido se entrega a los agentes que trabajan en la tarea.",
2625
2647
  "attach": "Adjuntar",
2626
2648
  "attached": "Incidencia adjuntada",
2627
2649
  "attachFailed": "No se pudo adjuntar",
@@ -2960,6 +2982,9 @@
2960
2982
  "findings": "Hallazgos",
2961
2983
  "open": "Abiertos",
2962
2984
  "answered": "Respondidos",
2985
+ "recommendations": "Recomendaciones",
2986
+ "recsGenerating": "Generando",
2987
+ "recsToReview": "Por revisar",
2963
2988
  "model": "Modelo"
2964
2989
  },
2965
2990
  "actions": {
@@ -3667,7 +3692,7 @@
3667
3692
  "sources": {
3668
3693
  "metaSynced": "sincronizado · ref {ref}",
3669
3694
  "metaNever": "nunca sincronizado · ref {ref}",
3670
- "changes": "{count} cambio | {count} cambios",
3695
+ "changes": "Cambios",
3671
3696
  "empty": "No hay repositorios de pautas vinculados. Vincula uno abajo para importar sus archivos Markdown como fragmentos.",
3672
3697
  "linkTitle": "Vincular un repositorio de pautas",
3673
3698
  "ownerPlaceholder": "propietario",
@@ -3692,7 +3717,7 @@
3692
3717
  "linkSourceFailed": "No se pudo vincular el origen",
3693
3718
  "synced": "Sincronizado: {updated} actualizados, {removed} eliminados",
3694
3719
  "syncFailed": "No se pudo sincronizar el origen",
3695
- "changesAvailable": "{count} cambio disponible | {count} cambios disponibles",
3720
+ "changesAvailable": "Cambios disponibles",
3696
3721
  "upToDate": "Al día",
3697
3722
  "checkSourceFailed": "No se pudo comprobar el origen",
3698
3723
  "sourceUnlinked": "Origen desvinculado",