@cat-factory/app 0.147.1 → 0.147.2

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.
@@ -1,6 +1,11 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import type { StepSubtasks } from '~/types/execution'
3
- import { activeChunkLabels, chunkReviewPercent, isSlicingChunks } from './prReviewProgress'
2
+ import type { PrReviewStepState, StepSubtasks } from '~/types/execution'
3
+ import {
4
+ activeChunkLabels,
5
+ chunkReviewPercent,
6
+ isSlicingChunks,
7
+ prReviewPhase,
8
+ } from './prReviewProgress'
4
9
 
5
10
  const subtasks = (over: Partial<StepSubtasks>): StepSubtasks => ({
6
11
  completed: 0,
@@ -71,3 +76,47 @@ describe('activeChunkLabels', () => {
71
76
  ).toEqual([])
72
77
  })
73
78
  })
79
+
80
+ // `prReviewPhase` reads only `status`; the other PrReviewStepState fields are irrelevant here.
81
+ const state = (status: PrReviewStepState['status']): PrReviewStepState =>
82
+ ({ status }) as PrReviewStepState
83
+
84
+ describe('prReviewPhase', () => {
85
+ it('is null with no live review or a terminal/passed-through status', () => {
86
+ expect(prReviewPhase(null, null)).toBeNull()
87
+ expect(prReviewPhase(undefined, subtasks({ total: 3, completed: 3 }))).toBeNull()
88
+ expect(prReviewPhase(state('done'), subtasks({ total: 3, completed: 3 }))).toBeNull()
89
+ expect(prReviewPhase(state('skipped'), null)).toBeNull()
90
+ })
91
+
92
+ it('is slicing while reviewing with no todo list yet (counts zeroed)', () => {
93
+ // No plan committed → don't leak a misleading 0/0 slice count.
94
+ expect(prReviewPhase(state('reviewing'), null)).toEqual({
95
+ kind: 'slicing',
96
+ completed: 0,
97
+ total: 0,
98
+ })
99
+ expect(prReviewPhase(state('reviewing'), subtasks({ total: 0 }))).toEqual({
100
+ kind: 'slicing',
101
+ completed: 0,
102
+ total: 0,
103
+ })
104
+ })
105
+
106
+ it('is reviewing with the slice counts once the todo list exists', () => {
107
+ expect(prReviewPhase(state('reviewing'), subtasks({ total: 4, completed: 1 }))).toEqual({
108
+ kind: 'reviewing',
109
+ completed: 1,
110
+ total: 4,
111
+ })
112
+ })
113
+
114
+ it('maps the parked / resolving statuses to their phase', () => {
115
+ expect(
116
+ prReviewPhase(state('awaiting_selection'), subtasks({ total: 4, completed: 4 }))?.kind,
117
+ ).toBe('awaiting')
118
+ expect(prReviewPhase(state('challenging'), null)?.kind).toBe('challenging')
119
+ expect(prReviewPhase(state('fixing'), null)?.kind).toBe('fixing')
120
+ expect(prReviewPhase(state('posting'), null)?.kind).toBe('posting')
121
+ })
122
+ })
@@ -1,4 +1,4 @@
1
- import type { StepSubtasks } from '~/types/execution'
1
+ import type { PrReviewStepState, StepSubtasks } from '~/types/execution'
2
2
 
3
3
  // Pure derivation of the PR deep-reviewer's `reviewing`-phase progress, factored out of
4
4
  // `PrReviewWindow.vue` so it is unit-testable independently of the Vue component.
@@ -26,3 +26,54 @@ export function chunkReviewPercent(subtasks: StepSubtasks | null | undefined): n
26
26
  export function activeChunkLabels(subtasks: StepSubtasks | null | undefined): string[] {
27
27
  return subtasks?.items?.filter((i) => i.status === 'in_progress').map((i) => i.label) ?? []
28
28
  }
29
+
30
+ /**
31
+ * The at-a-glance phase of a `pr-reviewer` step, collapsing its `prReview.status` (+ the
32
+ * slicing-vs-reviewing signal from the todo list) into a single kind the board surfaces label
33
+ * without re-deriving. `completed`/`total` carry the slice counts for the `reviewing` kind.
34
+ * Returns `null` for the terminal / passed-through states (`done`/`skipped`) and when there is
35
+ * no live review — those have no in-flight phase to show.
36
+ */
37
+ export type PrReviewPhaseKind =
38
+ | 'slicing'
39
+ | 'reviewing'
40
+ | 'awaiting'
41
+ | 'challenging'
42
+ | 'fixing'
43
+ | 'posting'
44
+
45
+ export interface PrReviewPhase {
46
+ kind: PrReviewPhaseKind
47
+ /** Slices whose review is finished (0 while slicing). */
48
+ completed: number
49
+ /** Total slices the reviewer grouped the diff into (0 while slicing). */
50
+ total: number
51
+ }
52
+
53
+ export function prReviewPhase(
54
+ state: PrReviewStepState | null | undefined,
55
+ subtasks: StepSubtasks | null | undefined,
56
+ ): PrReviewPhase | null {
57
+ const status = state?.status
58
+ if (!status) return null
59
+ const completed = subtasks?.completed ?? 0
60
+ const total = subtasks?.total ?? 0
61
+ switch (status) {
62
+ case 'reviewing':
63
+ // No todo list yet ⇒ still grouping the diff; otherwise working through the chunks.
64
+ return isSlicingChunks(subtasks)
65
+ ? { kind: 'slicing', completed: 0, total: 0 }
66
+ : { kind: 'reviewing', completed, total }
67
+ case 'awaiting_selection':
68
+ return { kind: 'awaiting', completed, total }
69
+ case 'challenging':
70
+ return { kind: 'challenging', completed, total }
71
+ case 'fixing':
72
+ return { kind: 'fixing', completed, total }
73
+ case 'posting':
74
+ return { kind: 'posting', completed, total }
75
+ default:
76
+ // done / skipped — no live phase to surface.
77
+ return null
78
+ }
79
+ }
@@ -4961,6 +4961,14 @@
4961
4961
  "pending": "In Warteschlange"
4962
4962
  }
4963
4963
  },
4964
+ "phase": {
4965
+ "slicing": "Wird aufgeteilt…",
4966
+ "reviewing": "Prüfe {completed}/{total} Abschnitte",
4967
+ "awaiting": "Ergebnisse bereit",
4968
+ "challenging": "Wird untersucht…",
4969
+ "fixing": "Wird behoben…",
4970
+ "posting": "Wird veröffentlicht…"
4971
+ },
4964
4972
  "challenge": {
4965
4973
  "action": "Anfechten",
4966
4974
  "reChallenge": "Erneut anfechten",
@@ -5090,6 +5090,14 @@
5090
5090
  "pending": "Queued"
5091
5091
  }
5092
5092
  },
5093
+ "phase": {
5094
+ "slicing": "Slicing…",
5095
+ "reviewing": "Reviewing {completed}/{total} slices",
5096
+ "awaiting": "Findings ready",
5097
+ "challenging": "Investigating…",
5098
+ "fixing": "Fixing…",
5099
+ "posting": "Posting…"
5100
+ },
5093
5101
  "challenge": {
5094
5102
  "action": "Challenge",
5095
5103
  "reChallenge": "Challenge again",
@@ -4949,6 +4949,14 @@
4949
4949
  "pending": "En cola"
4950
4950
  }
4951
4951
  },
4952
+ "phase": {
4953
+ "slicing": "Dividiendo…",
4954
+ "reviewing": "Revisando {completed}/{total} secciones",
4955
+ "awaiting": "Resultados listos",
4956
+ "challenging": "Investigando…",
4957
+ "fixing": "Corrigiendo…",
4958
+ "posting": "Publicando…"
4959
+ },
4952
4960
  "challenge": {
4953
4961
  "action": "Cuestionar",
4954
4962
  "reChallenge": "Cuestionar de nuevo",
@@ -4949,6 +4949,14 @@
4949
4949
  "pending": "En attente"
4950
4950
  }
4951
4951
  },
4952
+ "phase": {
4953
+ "slicing": "Découpage…",
4954
+ "reviewing": "Révision {completed}/{total} sections",
4955
+ "awaiting": "Résultats prêts",
4956
+ "challenging": "Analyse en cours…",
4957
+ "fixing": "Correction…",
4958
+ "posting": "Publication…"
4959
+ },
4952
4960
  "challenge": {
4953
4961
  "action": "Contester",
4954
4962
  "reChallenge": "Contester à nouveau",
@@ -4960,6 +4960,14 @@
4960
4960
  "pending": "בתור"
4961
4961
  }
4962
4962
  },
4963
+ "phase": {
4964
+ "slicing": "מחלק…",
4965
+ "reviewing": "בודק {completed}/{total} מקטעים",
4966
+ "awaiting": "הממצאים מוכנים",
4967
+ "challenging": "חוקר…",
4968
+ "fixing": "מתקן…",
4969
+ "posting": "מפרסם…"
4970
+ },
4963
4971
  "challenge": {
4964
4972
  "action": "ערער",
4965
4973
  "reChallenge": "ערער שוב",
@@ -4961,6 +4961,14 @@
4961
4961
  "pending": "In coda"
4962
4962
  }
4963
4963
  },
4964
+ "phase": {
4965
+ "slicing": "Suddivisione…",
4966
+ "reviewing": "Revisione {completed}/{total} sezioni",
4967
+ "awaiting": "Risultati pronti",
4968
+ "challenging": "Analisi in corso…",
4969
+ "fixing": "Correzione…",
4970
+ "posting": "Pubblicazione…"
4971
+ },
4964
4972
  "challenge": {
4965
4973
  "action": "Contesta",
4966
4974
  "reChallenge": "Contesta di nuovo",
@@ -4961,6 +4961,14 @@
4961
4961
  "pending": "待機中"
4962
4962
  }
4963
4963
  },
4964
+ "phase": {
4965
+ "slicing": "分割中…",
4966
+ "reviewing": "レビュー中 {completed}/{total} 区分",
4967
+ "awaiting": "指摘の準備完了",
4968
+ "challenging": "調査中…",
4969
+ "fixing": "修正中…",
4970
+ "posting": "投稿中…"
4971
+ },
4964
4972
  "challenge": {
4965
4973
  "action": "異議を唱える",
4966
4974
  "reChallenge": "再度異議を唱える",
@@ -4949,6 +4949,14 @@
4949
4949
  "pending": "W kolejce"
4950
4950
  }
4951
4951
  },
4952
+ "phase": {
4953
+ "slicing": "Dzielenie…",
4954
+ "reviewing": "Przegląd {completed}/{total} fragmentów",
4955
+ "awaiting": "Wyniki gotowe",
4956
+ "challenging": "Analizowanie…",
4957
+ "fixing": "Naprawianie…",
4958
+ "posting": "Publikowanie…"
4959
+ },
4952
4960
  "challenge": {
4953
4961
  "action": "Zakwestionuj",
4954
4962
  "reChallenge": "Zakwestionuj ponownie",
@@ -4961,6 +4961,14 @@
4961
4961
  "pending": "Sırada"
4962
4962
  }
4963
4963
  },
4964
+ "phase": {
4965
+ "slicing": "Dilimleniyor…",
4966
+ "reviewing": "İnceleniyor {completed}/{total} bölüm",
4967
+ "awaiting": "Bulgular hazır",
4968
+ "challenging": "Araştırılıyor…",
4969
+ "fixing": "Düzeltiliyor…",
4970
+ "posting": "Yayınlanıyor…"
4971
+ },
4964
4972
  "challenge": {
4965
4973
  "action": "İtiraz et",
4966
4974
  "reChallenge": "Tekrar itiraz et",
@@ -4949,6 +4949,14 @@
4949
4949
  "pending": "У черзі"
4950
4950
  }
4951
4951
  },
4952
+ "phase": {
4953
+ "slicing": "Розбиття…",
4954
+ "reviewing": "Перевірка {completed}/{total} фрагментів",
4955
+ "awaiting": "Результати готові",
4956
+ "challenging": "Дослідження…",
4957
+ "fixing": "Виправлення…",
4958
+ "posting": "Публікація…"
4959
+ },
4952
4960
  "challenge": {
4953
4961
  "action": "Оскаржити",
4954
4962
  "reChallenge": "Оскаржити знову",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.147.1",
3
+ "version": "0.147.2",
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",