@cat-factory/app 0.6.0 → 0.7.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.
- package/LICENSE +21 -21
- package/app/components/board/ContextPicker.vue +367 -367
- package/app/components/gates/GateResultView.vue +109 -4
- package/app/components/layout/SideBar.vue +11 -0
- package/app/components/observability/StepMetricsBar.vue +102 -102
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -178
- package/app/components/panels/inspector/TaskRunSettings.vue +77 -0
- package/app/components/recurring/RecurrenceEditor.vue +124 -124
- package/app/components/settings/IssueTrackerWritebackPanel.vue +103 -0
- package/app/composables/useBlockQueries.ts +154 -154
- package/app/composables/useContextLinking.ts +65 -65
- package/app/composables/useFrameResize.ts +54 -54
- package/app/pages/index.vue +2 -0
- package/app/stores/documents.ts +176 -176
- package/app/stores/services.ts +87 -87
- package/app/stores/tracker.ts +39 -27
- package/app/stores/ui.ts +12 -0
- package/app/types/documents.ts +104 -104
- package/app/types/domain.ts +5 -1
- package/app/types/execution.ts +18 -0
- package/app/types/github.ts +173 -173
- package/app/types/services.ts +27 -27
- package/app/types/tasks.ts +82 -82
- package/app/types/tracker.ts +27 -18
- package/app/utils/agentOutput.spec.ts +128 -128
- package/app/utils/agentOutput.ts +173 -173
- package/app/utils/observability.ts +52 -52
- package/package.json +6 -1
package/app/stores/services.ts
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import { defineStore } from 'pinia'
|
|
2
|
-
import { computed, ref } from 'vue'
|
|
3
|
-
import type { Service, WorkspaceMount } from '~/types/services'
|
|
4
|
-
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* In-org shared services. A `Service` is account-owned (a service frame + its subtree + repo)
|
|
8
|
-
* and can be mounted onto several teams' boards; a `WorkspaceMount` places one onto THIS
|
|
9
|
-
* board with its own frame layout. Hydrated from the workspace snapshot:
|
|
10
|
-
* - `mounts` — the services this board mounts (drives the per-board frame layout),
|
|
11
|
-
* - `catalog` — the org's services this board can mount from (each with a `mountCount`).
|
|
12
|
-
*/
|
|
13
|
-
export const useServicesStore = defineStore('services', () => {
|
|
14
|
-
const api = useApi()
|
|
15
|
-
|
|
16
|
-
const mounts = ref<WorkspaceMount[]>([])
|
|
17
|
-
const catalog = ref<Service[]>([])
|
|
18
|
-
|
|
19
|
-
function hydrate(nextMounts: WorkspaceMount[], nextCatalog: Service[]) {
|
|
20
|
-
mounts.value = [...nextMounts]
|
|
21
|
-
catalog.value = [...nextCatalog]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Mount row keyed by service id. */
|
|
25
|
-
const byServiceId = computed<Record<string, WorkspaceMount>>(() => {
|
|
26
|
-
const map: Record<string, WorkspaceMount> = {}
|
|
27
|
-
for (const m of mounts.value) map[m.serviceId] = m
|
|
28
|
-
return map
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
/** Catalog service keyed by its frame block id (resolve a frame → its service). */
|
|
32
|
-
const serviceByFrameBlock = computed<Record<string, Service>>(() => {
|
|
33
|
-
const map: Record<string, Service> = {}
|
|
34
|
-
for (const s of catalog.value) map[s.frameBlockId] = s
|
|
35
|
-
return map
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
/** Org services NOT yet mounted on this board (the "add existing service" picker's options). */
|
|
39
|
-
const mountable = computed<Service[]>(() => {
|
|
40
|
-
const mounted = new Set(mounts.value.map((m) => m.serviceId))
|
|
41
|
-
return catalog.value.filter((s) => !mounted.has(s.id))
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
/** A frame is "shared" when its service is mounted on more than one board. */
|
|
45
|
-
function isSharedFrame(frameBlockId: string): boolean {
|
|
46
|
-
return (serviceByFrameBlock.value[frameBlockId]?.mountCount ?? 0) > 1
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function mount(serviceId: string, position?: { x: number; y: number }) {
|
|
50
|
-
const ws = useWorkspaceStore()
|
|
51
|
-
const created = await api.mountService(ws.requireId(), serviceId, position ? { position } : {})
|
|
52
|
-
await ws.refresh()
|
|
53
|
-
return created
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function unmount(serviceId: string) {
|
|
57
|
-
const ws = useWorkspaceStore()
|
|
58
|
-
await api.unmountService(ws.requireId(), serviceId)
|
|
59
|
-
await ws.refresh()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Persist a mounted frame's per-board layout (called on frame drag/resize end). */
|
|
63
|
-
async function updateLayout(
|
|
64
|
-
serviceId: string,
|
|
65
|
-
position?: { x: number; y: number },
|
|
66
|
-
size?: { w: number; h: number } | null,
|
|
67
|
-
) {
|
|
68
|
-
const ws = useWorkspaceStore()
|
|
69
|
-
const updated = await api.updateMountLayout(ws.requireId(), serviceId, { position, size })
|
|
70
|
-
const local = mounts.value.find((m) => m.serviceId === serviceId)
|
|
71
|
-
if (local) Object.assign(local, updated)
|
|
72
|
-
return updated
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
mounts,
|
|
77
|
-
catalog,
|
|
78
|
-
byServiceId,
|
|
79
|
-
serviceByFrameBlock,
|
|
80
|
-
mountable,
|
|
81
|
-
isSharedFrame,
|
|
82
|
-
hydrate,
|
|
83
|
-
mount,
|
|
84
|
-
unmount,
|
|
85
|
-
updateLayout,
|
|
86
|
-
}
|
|
87
|
-
})
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
import type { Service, WorkspaceMount } from '~/types/services'
|
|
4
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* In-org shared services. A `Service` is account-owned (a service frame + its subtree + repo)
|
|
8
|
+
* and can be mounted onto several teams' boards; a `WorkspaceMount` places one onto THIS
|
|
9
|
+
* board with its own frame layout. Hydrated from the workspace snapshot:
|
|
10
|
+
* - `mounts` — the services this board mounts (drives the per-board frame layout),
|
|
11
|
+
* - `catalog` — the org's services this board can mount from (each with a `mountCount`).
|
|
12
|
+
*/
|
|
13
|
+
export const useServicesStore = defineStore('services', () => {
|
|
14
|
+
const api = useApi()
|
|
15
|
+
|
|
16
|
+
const mounts = ref<WorkspaceMount[]>([])
|
|
17
|
+
const catalog = ref<Service[]>([])
|
|
18
|
+
|
|
19
|
+
function hydrate(nextMounts: WorkspaceMount[], nextCatalog: Service[]) {
|
|
20
|
+
mounts.value = [...nextMounts]
|
|
21
|
+
catalog.value = [...nextCatalog]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Mount row keyed by service id. */
|
|
25
|
+
const byServiceId = computed<Record<string, WorkspaceMount>>(() => {
|
|
26
|
+
const map: Record<string, WorkspaceMount> = {}
|
|
27
|
+
for (const m of mounts.value) map[m.serviceId] = m
|
|
28
|
+
return map
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/** Catalog service keyed by its frame block id (resolve a frame → its service). */
|
|
32
|
+
const serviceByFrameBlock = computed<Record<string, Service>>(() => {
|
|
33
|
+
const map: Record<string, Service> = {}
|
|
34
|
+
for (const s of catalog.value) map[s.frameBlockId] = s
|
|
35
|
+
return map
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/** Org services NOT yet mounted on this board (the "add existing service" picker's options). */
|
|
39
|
+
const mountable = computed<Service[]>(() => {
|
|
40
|
+
const mounted = new Set(mounts.value.map((m) => m.serviceId))
|
|
41
|
+
return catalog.value.filter((s) => !mounted.has(s.id))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/** A frame is "shared" when its service is mounted on more than one board. */
|
|
45
|
+
function isSharedFrame(frameBlockId: string): boolean {
|
|
46
|
+
return (serviceByFrameBlock.value[frameBlockId]?.mountCount ?? 0) > 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function mount(serviceId: string, position?: { x: number; y: number }) {
|
|
50
|
+
const ws = useWorkspaceStore()
|
|
51
|
+
const created = await api.mountService(ws.requireId(), serviceId, position ? { position } : {})
|
|
52
|
+
await ws.refresh()
|
|
53
|
+
return created
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function unmount(serviceId: string) {
|
|
57
|
+
const ws = useWorkspaceStore()
|
|
58
|
+
await api.unmountService(ws.requireId(), serviceId)
|
|
59
|
+
await ws.refresh()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Persist a mounted frame's per-board layout (called on frame drag/resize end). */
|
|
63
|
+
async function updateLayout(
|
|
64
|
+
serviceId: string,
|
|
65
|
+
position?: { x: number; y: number },
|
|
66
|
+
size?: { w: number; h: number } | null,
|
|
67
|
+
) {
|
|
68
|
+
const ws = useWorkspaceStore()
|
|
69
|
+
const updated = await api.updateMountLayout(ws.requireId(), serviceId, { position, size })
|
|
70
|
+
const local = mounts.value.find((m) => m.serviceId === serviceId)
|
|
71
|
+
if (local) Object.assign(local, updated)
|
|
72
|
+
return updated
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
mounts,
|
|
77
|
+
catalog,
|
|
78
|
+
byServiceId,
|
|
79
|
+
serviceByFrameBlock,
|
|
80
|
+
mountable,
|
|
81
|
+
isSharedFrame,
|
|
82
|
+
hydrate,
|
|
83
|
+
mount,
|
|
84
|
+
unmount,
|
|
85
|
+
updateLayout,
|
|
86
|
+
}
|
|
87
|
+
})
|
package/app/stores/tracker.ts
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
|
-
import { defineStore } from 'pinia'
|
|
2
|
-
import { ref } from 'vue'
|
|
3
|
-
import type { PutTrackerSettingsInput, TrackerSettings } from '~/types/tracker'
|
|
4
|
-
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* The workspace's issue-tracker selection (GitHub Issues or Jira) — where the
|
|
8
|
-
* tech-debt recurring pipeline files its ticket. Hydrated from the snapshot;
|
|
9
|
-
* edited inline when configuring a tech-debt recurring pipeline.
|
|
10
|
-
*/
|
|
11
|
-
export const useTrackerStore = defineStore('tracker', () => {
|
|
12
|
-
const api = useApi()
|
|
13
|
-
|
|
14
|
-
const settings = ref<TrackerSettings>({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import type { PutTrackerSettingsInput, TrackerSettings } from '~/types/tracker'
|
|
4
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The workspace's issue-tracker selection (GitHub Issues or Jira) — where the
|
|
8
|
+
* tech-debt recurring pipeline files its ticket. Hydrated from the snapshot;
|
|
9
|
+
* edited inline when configuring a tech-debt recurring pipeline.
|
|
10
|
+
*/
|
|
11
|
+
export const useTrackerStore = defineStore('tracker', () => {
|
|
12
|
+
const api = useApi()
|
|
13
|
+
|
|
14
|
+
const settings = ref<TrackerSettings>({
|
|
15
|
+
tracker: null,
|
|
16
|
+
jiraProjectKey: null,
|
|
17
|
+
writebackCommentOnPrOpen: false,
|
|
18
|
+
writebackResolveOnMerge: false,
|
|
19
|
+
updatedAt: 0,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
function hydrate(value: TrackerSettings | undefined) {
|
|
23
|
+
settings.value = value ?? {
|
|
24
|
+
tracker: null,
|
|
25
|
+
jiraProjectKey: null,
|
|
26
|
+
writebackCommentOnPrOpen: false,
|
|
27
|
+
writebackResolveOnMerge: false,
|
|
28
|
+
updatedAt: 0,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function save(input: PutTrackerSettingsInput) {
|
|
33
|
+
const ws = useWorkspaceStore()
|
|
34
|
+
settings.value = await api.putTrackerSettings(ws.requireId(), input)
|
|
35
|
+
return settings.value
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { settings, hydrate, save }
|
|
39
|
+
})
|
package/app/stores/ui.ts
CHANGED
|
@@ -70,6 +70,9 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
70
70
|
// Workspace-settings panel: the run-timing escalation threshold + per-service task limit.
|
|
71
71
|
const workspaceSettingsOpen = ref(false)
|
|
72
72
|
const datadogOpen = ref(false)
|
|
73
|
+
// Workspace-settings panel: issue-tracker writeback toggles (comment on PR open,
|
|
74
|
+
// close linked issue on merge).
|
|
75
|
+
const issueWritebackOpen = ref(false)
|
|
73
76
|
const modelDefaultsOpen = ref(false)
|
|
74
77
|
// Workspace-settings panel: the default service-fragment selection new services inherit.
|
|
75
78
|
const serviceFragmentDefaultsOpen = ref(false)
|
|
@@ -287,6 +290,12 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
287
290
|
function closeWorkspaceSettings() {
|
|
288
291
|
workspaceSettingsOpen.value = false
|
|
289
292
|
}
|
|
293
|
+
function openIssueWriteback() {
|
|
294
|
+
issueWritebackOpen.value = true
|
|
295
|
+
}
|
|
296
|
+
function closeIssueWriteback() {
|
|
297
|
+
issueWritebackOpen.value = false
|
|
298
|
+
}
|
|
290
299
|
function openDatadog() {
|
|
291
300
|
datadogOpen.value = true
|
|
292
301
|
}
|
|
@@ -360,6 +369,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
360
369
|
fragmentLibraryOpen,
|
|
361
370
|
commandBarOpen,
|
|
362
371
|
mergeThresholdsOpen,
|
|
372
|
+
issueWritebackOpen,
|
|
363
373
|
workspaceSettingsOpen,
|
|
364
374
|
datadogOpen,
|
|
365
375
|
modelDefaultsOpen,
|
|
@@ -411,6 +421,8 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
411
421
|
toggleCommandBar,
|
|
412
422
|
openMergeThresholds,
|
|
413
423
|
closeMergeThresholds,
|
|
424
|
+
openIssueWriteback,
|
|
425
|
+
closeIssueWriteback,
|
|
414
426
|
openWorkspaceSettings,
|
|
415
427
|
closeWorkspaceSettings,
|
|
416
428
|
openDatadog,
|
package/app/types/documents.ts
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Document-source integration. Requirements / RFCs / PRDs imported from external
|
|
3
|
-
// sources (Confluence, Notion, …) can be expanded into board structure or
|
|
4
|
-
// attached to a task as agent context. These mirror the `@cat-factory/contracts`
|
|
5
|
-
// document schemas; the abstraction is source-agnostic, keyed by `source`.
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
|
-
import type { BlockType } from './domain'
|
|
9
|
-
|
|
10
|
-
/** The external document sources cat-factory can link to. */
|
|
11
|
-
export type DocumentSourceKind = 'confluence' | 'notion' | 'github'
|
|
12
|
-
|
|
13
|
-
/** One credential a provider needs to connect (rendered as a form field). */
|
|
14
|
-
export interface CredentialField {
|
|
15
|
-
key: string
|
|
16
|
-
label: string
|
|
17
|
-
help?: string
|
|
18
|
-
placeholder?: string
|
|
19
|
-
secret?: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** A source's self-description: drives the generic connect + import UI. */
|
|
23
|
-
export interface DocumentSourceDescriptor {
|
|
24
|
-
source: DocumentSourceKind
|
|
25
|
-
label: string
|
|
26
|
-
/** Lucide icon name for the source. */
|
|
27
|
-
icon: string
|
|
28
|
-
credentialFields: CredentialField[]
|
|
29
|
-
refLabel: string
|
|
30
|
-
refPlaceholder: string
|
|
31
|
-
/** Whether the source supports searching its catalogue by title/content. */
|
|
32
|
-
searchable?: boolean
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** A workspace's connection to a document source (never carries credentials). */
|
|
36
|
-
export interface DocumentConnection {
|
|
37
|
-
source: DocumentSourceKind
|
|
38
|
-
/** Human-friendly label for what we're connected to (site URL, workspace name). */
|
|
39
|
-
label: string
|
|
40
|
-
/** When the connection was established (epoch ms). */
|
|
41
|
-
connectedAt: number
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** A page imported from a source into the workspace. */
|
|
45
|
-
export interface SourceDocument {
|
|
46
|
-
source: DocumentSourceKind
|
|
47
|
-
/** The source's stable id for the page. */
|
|
48
|
-
externalId: string
|
|
49
|
-
title: string
|
|
50
|
-
url: string
|
|
51
|
-
/** Short plain-text preview of the page body. */
|
|
52
|
-
excerpt: string
|
|
53
|
-
/** The board block this document is attached to as context, if any. */
|
|
54
|
-
linkedBlockId: string | null
|
|
55
|
-
syncedAt: number
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** A lean hit from searching a document source's catalogue (not yet imported). */
|
|
59
|
-
export interface DocumentSearchResult {
|
|
60
|
-
source: DocumentSourceKind
|
|
61
|
-
/** The source's stable id for the page (re-usable as an import ref). */
|
|
62
|
-
externalId: string
|
|
63
|
-
title: string
|
|
64
|
-
url: string
|
|
65
|
-
/** Short plain-text preview (may be empty). */
|
|
66
|
-
excerpt: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** A proposed task within a planned frame/module. */
|
|
70
|
-
export interface PlanTask {
|
|
71
|
-
title: string
|
|
72
|
-
description?: string
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** A proposed module grouping tasks within a planned frame. */
|
|
76
|
-
export interface PlanModule {
|
|
77
|
-
name: string
|
|
78
|
-
tasks: PlanTask[]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** A proposed top-level frame with its modules and loose tasks. */
|
|
82
|
-
export interface PlanFrame {
|
|
83
|
-
type: BlockType
|
|
84
|
-
title: string
|
|
85
|
-
description?: string
|
|
86
|
-
modules: PlanModule[]
|
|
87
|
-
tasks: PlanTask[]
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** A board structure extracted from an imported document. */
|
|
91
|
-
export interface DocumentBoardPlan {
|
|
92
|
-
source: DocumentSourceKind
|
|
93
|
-
externalId: string
|
|
94
|
-
/** Whether an LLM produced the plan or the deterministic heading parser did. */
|
|
95
|
-
planner: 'llm' | 'headings'
|
|
96
|
-
frames: PlanFrame[]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Counts of blocks created by spawning a plan onto the board. */
|
|
100
|
-
export interface SpawnResult {
|
|
101
|
-
frames: number
|
|
102
|
-
modules: number
|
|
103
|
-
tasks: number
|
|
104
|
-
}
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Document-source integration. Requirements / RFCs / PRDs imported from external
|
|
3
|
+
// sources (Confluence, Notion, …) can be expanded into board structure or
|
|
4
|
+
// attached to a task as agent context. These mirror the `@cat-factory/contracts`
|
|
5
|
+
// document schemas; the abstraction is source-agnostic, keyed by `source`.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import type { BlockType } from './domain'
|
|
9
|
+
|
|
10
|
+
/** The external document sources cat-factory can link to. */
|
|
11
|
+
export type DocumentSourceKind = 'confluence' | 'notion' | 'github'
|
|
12
|
+
|
|
13
|
+
/** One credential a provider needs to connect (rendered as a form field). */
|
|
14
|
+
export interface CredentialField {
|
|
15
|
+
key: string
|
|
16
|
+
label: string
|
|
17
|
+
help?: string
|
|
18
|
+
placeholder?: string
|
|
19
|
+
secret?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A source's self-description: drives the generic connect + import UI. */
|
|
23
|
+
export interface DocumentSourceDescriptor {
|
|
24
|
+
source: DocumentSourceKind
|
|
25
|
+
label: string
|
|
26
|
+
/** Lucide icon name for the source. */
|
|
27
|
+
icon: string
|
|
28
|
+
credentialFields: CredentialField[]
|
|
29
|
+
refLabel: string
|
|
30
|
+
refPlaceholder: string
|
|
31
|
+
/** Whether the source supports searching its catalogue by title/content. */
|
|
32
|
+
searchable?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A workspace's connection to a document source (never carries credentials). */
|
|
36
|
+
export interface DocumentConnection {
|
|
37
|
+
source: DocumentSourceKind
|
|
38
|
+
/** Human-friendly label for what we're connected to (site URL, workspace name). */
|
|
39
|
+
label: string
|
|
40
|
+
/** When the connection was established (epoch ms). */
|
|
41
|
+
connectedAt: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A page imported from a source into the workspace. */
|
|
45
|
+
export interface SourceDocument {
|
|
46
|
+
source: DocumentSourceKind
|
|
47
|
+
/** The source's stable id for the page. */
|
|
48
|
+
externalId: string
|
|
49
|
+
title: string
|
|
50
|
+
url: string
|
|
51
|
+
/** Short plain-text preview of the page body. */
|
|
52
|
+
excerpt: string
|
|
53
|
+
/** The board block this document is attached to as context, if any. */
|
|
54
|
+
linkedBlockId: string | null
|
|
55
|
+
syncedAt: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A lean hit from searching a document source's catalogue (not yet imported). */
|
|
59
|
+
export interface DocumentSearchResult {
|
|
60
|
+
source: DocumentSourceKind
|
|
61
|
+
/** The source's stable id for the page (re-usable as an import ref). */
|
|
62
|
+
externalId: string
|
|
63
|
+
title: string
|
|
64
|
+
url: string
|
|
65
|
+
/** Short plain-text preview (may be empty). */
|
|
66
|
+
excerpt: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A proposed task within a planned frame/module. */
|
|
70
|
+
export interface PlanTask {
|
|
71
|
+
title: string
|
|
72
|
+
description?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** A proposed module grouping tasks within a planned frame. */
|
|
76
|
+
export interface PlanModule {
|
|
77
|
+
name: string
|
|
78
|
+
tasks: PlanTask[]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A proposed top-level frame with its modules and loose tasks. */
|
|
82
|
+
export interface PlanFrame {
|
|
83
|
+
type: BlockType
|
|
84
|
+
title: string
|
|
85
|
+
description?: string
|
|
86
|
+
modules: PlanModule[]
|
|
87
|
+
tasks: PlanTask[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A board structure extracted from an imported document. */
|
|
91
|
+
export interface DocumentBoardPlan {
|
|
92
|
+
source: DocumentSourceKind
|
|
93
|
+
externalId: string
|
|
94
|
+
/** Whether an LLM produced the plan or the deterministic heading parser did. */
|
|
95
|
+
planner: 'llm' | 'headings'
|
|
96
|
+
frames: PlanFrame[]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Counts of blocks created by spawning a plan onto the board. */
|
|
100
|
+
export interface SpawnResult {
|
|
101
|
+
frames: number
|
|
102
|
+
modules: number
|
|
103
|
+
tasks: number
|
|
104
|
+
}
|
package/app/types/domain.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { ClarityReview } from './clarity'
|
|
|
23
23
|
import type { MergeThresholdPreset } from './merge'
|
|
24
24
|
import type { PipelineSchedule } from './recurring'
|
|
25
25
|
import type { Service, WorkspaceMount } from './services'
|
|
26
|
-
import type { TrackerSettings } from './tracker'
|
|
26
|
+
import type { TrackerSettings, WritebackOverride } from './tracker'
|
|
27
27
|
|
|
28
28
|
/** Lifecycle of an architecture building block. */
|
|
29
29
|
export type BlockStatus =
|
|
@@ -145,6 +145,10 @@ export interface Block {
|
|
|
145
145
|
* with the `product` role, notified when requirement review flags this task.
|
|
146
146
|
*/
|
|
147
147
|
responsibleProductUserId?: string | null
|
|
148
|
+
/** task-only: override "comment on the linked issue when a PR opens"; absent/null = inherit workspace. */
|
|
149
|
+
trackerCommentOnPrOpen?: WritebackOverride | null
|
|
150
|
+
/** task-only: override "close the linked issue as resolved on merge"; absent/null = inherit workspace. */
|
|
151
|
+
trackerResolveOnMerge?: WritebackOverride | null
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
/**
|
package/app/types/execution.ts
CHANGED
|
@@ -333,6 +333,22 @@ export interface PipelineStep {
|
|
|
333
333
|
export interface GateFailingCheck {
|
|
334
334
|
name: string
|
|
335
335
|
conclusion: string | null
|
|
336
|
+
/** GitHub web URL of the check run, so the UI can link to the failed run's logs */
|
|
337
|
+
url?: string | null
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** One helper-agent attempt the gate dispatched (mirrors `gateAttemptSchema`). */
|
|
341
|
+
export interface GateAttempt {
|
|
342
|
+
/** 1-based attempt number */
|
|
343
|
+
attempt: number
|
|
344
|
+
/** epoch ms when the helper job finished */
|
|
345
|
+
at: number
|
|
346
|
+
/** how the helper job ended */
|
|
347
|
+
outcome: 'completed' | 'failed'
|
|
348
|
+
/** the PR head commit the helper worked against, when known */
|
|
349
|
+
headSha?: string | null
|
|
350
|
+
/** the helper's own account of what it did / what remains */
|
|
351
|
+
summary?: string | null
|
|
336
352
|
}
|
|
337
353
|
|
|
338
354
|
/** Live state of a polling gate step (`ci` / `conflicts`); mirrors `gateStepStateSchema`. */
|
|
@@ -350,6 +366,8 @@ export interface GateStepState {
|
|
|
350
366
|
lastFailureSummary?: string | null
|
|
351
367
|
/** structured failing checks behind the summary (CI gate only; absent for conflicts) */
|
|
352
368
|
failingChecks?: GateFailingCheck[] | null
|
|
369
|
+
/** history of the helper-agent attempts this gate dispatched, newest last */
|
|
370
|
+
attemptLog?: GateAttempt[] | null
|
|
353
371
|
}
|
|
354
372
|
|
|
355
373
|
/** Live state of a `tester` step's Tester→Fixer loop (mirrors `testerStepStateSchema`). */
|