@cat-factory/app 0.6.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 (189) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/app/app.config.ts +8 -0
  4. package/app/app.vue +11 -0
  5. package/app/assets/css/main.css +100 -0
  6. package/app/components/auth/AuthGate.vue +24 -0
  7. package/app/components/auth/LoginScreen.vue +143 -0
  8. package/app/components/auth/UserMenu.vue +39 -0
  9. package/app/components/board/AddTaskModal.vue +444 -0
  10. package/app/components/board/AgentFailureCard.vue +97 -0
  11. package/app/components/board/AgentStopButton.vue +61 -0
  12. package/app/components/board/BoardCanvas.vue +183 -0
  13. package/app/components/board/ContextPicker.vue +367 -0
  14. package/app/components/board/RecurringPipelineModal.vue +219 -0
  15. package/app/components/board/TaskDependencyEdges.vue +132 -0
  16. package/app/components/board/nodes/AgentChip.vue +59 -0
  17. package/app/components/board/nodes/BlockNode.vue +433 -0
  18. package/app/components/board/nodes/DecisionBadge.vue +27 -0
  19. package/app/components/board/nodes/DraggableTask.vue +48 -0
  20. package/app/components/board/nodes/ModuleFrame.vue +97 -0
  21. package/app/components/board/nodes/TaskCard.vue +359 -0
  22. package/app/components/board/nodes/TaskPipelineMini.vue +159 -0
  23. package/app/components/bootstrap/BootstrapModal.vue +665 -0
  24. package/app/components/clarity/ClarityReviewWindow.vue +611 -0
  25. package/app/components/consensus/ConsensusSessionWindow.vue +210 -0
  26. package/app/components/documents/DocumentImportModal.vue +161 -0
  27. package/app/components/documents/DocumentSourceConnectModal.vue +127 -0
  28. package/app/components/documents/SpawnPreviewModal.vue +161 -0
  29. package/app/components/documents/TaskContextDocs.vue +83 -0
  30. package/app/components/focus/BlockFocusView.vue +171 -0
  31. package/app/components/fragments/FragmentLibraryPanel.vue +340 -0
  32. package/app/components/gates/GateResultView.vue +282 -0
  33. package/app/components/github/AddServiceFromRepoModal.vue +354 -0
  34. package/app/components/github/GitHubConnect.vue +183 -0
  35. package/app/components/github/GitHubOnboarding.vue +45 -0
  36. package/app/components/github/GitHubPanel.vue +584 -0
  37. package/app/components/github/RepoTreeBrowser.vue +171 -0
  38. package/app/components/layout/AccountTeamSettings.vue +237 -0
  39. package/app/components/layout/BoardSwitcher.vue +280 -0
  40. package/app/components/layout/BoardToolbar.vue +156 -0
  41. package/app/components/layout/CommandBar.vue +336 -0
  42. package/app/components/layout/GitHubPatBanner.vue +73 -0
  43. package/app/components/layout/NotificationsInbox.vue +175 -0
  44. package/app/components/layout/SideBar.vue +314 -0
  45. package/app/components/layout/SpendWarningBanner.vue +107 -0
  46. package/app/components/observability/StepMetricsBar.vue +102 -0
  47. package/app/components/palettes/AgentPalette.vue +86 -0
  48. package/app/components/panels/AgentStepDetail.vue +737 -0
  49. package/app/components/panels/DecisionModal.vue +71 -0
  50. package/app/components/panels/InspectorPanel.vue +465 -0
  51. package/app/components/panels/ObservabilityPanel.vue +351 -0
  52. package/app/components/panels/StepMetadataCard.vue +253 -0
  53. package/app/components/panels/StepRestartControl.vue +90 -0
  54. package/app/components/panels/StepResultViewHost.vue +40 -0
  55. package/app/components/panels/StepTestReport.vue +84 -0
  56. package/app/components/panels/inspector/ContainerSummary.vue +74 -0
  57. package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -0
  58. package/app/components/panels/inspector/ServiceFragments.vue +82 -0
  59. package/app/components/panels/inspector/ServiceTestConfig.vue +198 -0
  60. package/app/components/panels/inspector/TaskAgentConfig.vue +81 -0
  61. package/app/components/panels/inspector/TaskDependencies.vue +70 -0
  62. package/app/components/panels/inspector/TaskEstimateBadge.vue +56 -0
  63. package/app/components/panels/inspector/TaskExecution.vue +364 -0
  64. package/app/components/panels/inspector/TaskRunSettings.vue +187 -0
  65. package/app/components/panels/inspector/TaskStructure.vue +96 -0
  66. package/app/components/pipeline/AgentKindIcon.vue +30 -0
  67. package/app/components/pipeline/IterationCapPrompt.vue +70 -0
  68. package/app/components/pipeline/PipelineBuilder.vue +817 -0
  69. package/app/components/pipeline/PipelineProgress.vue +484 -0
  70. package/app/components/providers/ApiKeysSection.vue +273 -0
  71. package/app/components/providers/PersonalCredentialModal.vue +128 -0
  72. package/app/components/providers/PersonalSubscriptionSection.vue +225 -0
  73. package/app/components/providers/VendorCredentialsModal.vue +197 -0
  74. package/app/components/recurring/RecurrenceEditor.vue +124 -0
  75. package/app/components/requirements/RequirementsReviewWindow.vue +620 -0
  76. package/app/components/settings/DatadogPanel.vue +213 -0
  77. package/app/components/settings/LocalModelEndpointsPanel.vue +286 -0
  78. package/app/components/settings/MergeThresholdsPanel.vue +378 -0
  79. package/app/components/settings/ModelDefaultsPanel.vue +250 -0
  80. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +124 -0
  81. package/app/components/settings/WorkspaceSettingsPanel.vue +142 -0
  82. package/app/components/slack/SlackPanel.vue +299 -0
  83. package/app/components/tasks/TaskContextIssues.vue +88 -0
  84. package/app/components/tasks/TaskImportModal.vue +207 -0
  85. package/app/components/tasks/TaskSourceConnectModal.vue +133 -0
  86. package/app/components/testing/TestReportWindow.vue +404 -0
  87. package/app/composables/api/accounts.ts +81 -0
  88. package/app/composables/api/auth.ts +45 -0
  89. package/app/composables/api/board.ts +101 -0
  90. package/app/composables/api/bootstrap.ts +62 -0
  91. package/app/composables/api/context.ts +25 -0
  92. package/app/composables/api/documents.ts +74 -0
  93. package/app/composables/api/execution.ts +127 -0
  94. package/app/composables/api/fragments.ts +71 -0
  95. package/app/composables/api/github.ts +131 -0
  96. package/app/composables/api/models.ts +127 -0
  97. package/app/composables/api/notifications.ts +23 -0
  98. package/app/composables/api/presets.ts +29 -0
  99. package/app/composables/api/recurring.ts +68 -0
  100. package/app/composables/api/releaseHealth.ts +43 -0
  101. package/app/composables/api/reviews.ts +146 -0
  102. package/app/composables/api/slack.ts +54 -0
  103. package/app/composables/api/tasks.ts +72 -0
  104. package/app/composables/api/workspaces.ts +36 -0
  105. package/app/composables/useApi.ts +89 -0
  106. package/app/composables/useBlockDrag.ts +90 -0
  107. package/app/composables/useBlockQueries.ts +154 -0
  108. package/app/composables/useBoardFlow.ts +11 -0
  109. package/app/composables/useContextLinking.ts +65 -0
  110. package/app/composables/useDepLabels.ts +26 -0
  111. package/app/composables/useFrameResize.ts +54 -0
  112. package/app/composables/useResultView.ts +48 -0
  113. package/app/composables/useReviewStage.ts +40 -0
  114. package/app/composables/useSemanticZoom.ts +31 -0
  115. package/app/composables/useStepApproval.ts +233 -0
  116. package/app/composables/useStepProse.ts +78 -0
  117. package/app/composables/useStepTimer.ts +63 -0
  118. package/app/composables/useTaskExpansion.ts +92 -0
  119. package/app/composables/useWorkspaceStream.ts +155 -0
  120. package/app/docs/architecture.md +31 -0
  121. package/app/pages/index.vue +141 -0
  122. package/app/stores/accounts.ts +152 -0
  123. package/app/stores/agentConfig.ts +35 -0
  124. package/app/stores/agentRuns.ts +122 -0
  125. package/app/stores/agents.ts +40 -0
  126. package/app/stores/apiKeys.ts +108 -0
  127. package/app/stores/auth.ts +166 -0
  128. package/app/stores/board.spec.ts +205 -0
  129. package/app/stores/board.ts +286 -0
  130. package/app/stores/bootstrap.ts +97 -0
  131. package/app/stores/clarity.ts +196 -0
  132. package/app/stores/consensus.ts +60 -0
  133. package/app/stores/documents.ts +176 -0
  134. package/app/stores/execution.ts +273 -0
  135. package/app/stores/fragmentLibrary.ts +147 -0
  136. package/app/stores/fragments.ts +40 -0
  137. package/app/stores/github.ts +305 -0
  138. package/app/stores/localModels.ts +51 -0
  139. package/app/stores/mergePresets.ts +58 -0
  140. package/app/stores/modelDefaults.ts +76 -0
  141. package/app/stores/models.ts +134 -0
  142. package/app/stores/notifications.ts +70 -0
  143. package/app/stores/observability.ts +144 -0
  144. package/app/stores/personalSubscriptions.ts +215 -0
  145. package/app/stores/pipelines.ts +327 -0
  146. package/app/stores/recurringPipelines.ts +112 -0
  147. package/app/stores/releaseHealth.ts +75 -0
  148. package/app/stores/requirements.spec.ts +94 -0
  149. package/app/stores/requirements.ts +208 -0
  150. package/app/stores/serviceFragmentDefaults.ts +29 -0
  151. package/app/stores/services.ts +87 -0
  152. package/app/stores/slack.ts +142 -0
  153. package/app/stores/taskExpansion.ts +36 -0
  154. package/app/stores/tasks.spec.ts +71 -0
  155. package/app/stores/tasks.ts +176 -0
  156. package/app/stores/tracker.ts +27 -0
  157. package/app/stores/ui.ts +434 -0
  158. package/app/stores/vendorCredentials.ts +54 -0
  159. package/app/stores/workspace.ts +215 -0
  160. package/app/stores/workspaceSettings.ts +36 -0
  161. package/app/types/accounts.ts +77 -0
  162. package/app/types/bootstrap.ts +83 -0
  163. package/app/types/clarity.ts +59 -0
  164. package/app/types/consensus.ts +91 -0
  165. package/app/types/documents.ts +104 -0
  166. package/app/types/domain.ts +495 -0
  167. package/app/types/execution.ts +383 -0
  168. package/app/types/fragments.ts +72 -0
  169. package/app/types/github.ts +173 -0
  170. package/app/types/localModels.ts +73 -0
  171. package/app/types/merge.ts +71 -0
  172. package/app/types/models.ts +157 -0
  173. package/app/types/notifications.ts +74 -0
  174. package/app/types/recurring.ts +69 -0
  175. package/app/types/releaseHealth.ts +31 -0
  176. package/app/types/requirements.ts +61 -0
  177. package/app/types/services.ts +27 -0
  178. package/app/types/slack.ts +57 -0
  179. package/app/types/tasks.ts +82 -0
  180. package/app/types/tracker.ts +18 -0
  181. package/app/utils/agentOutput.spec.ts +128 -0
  182. package/app/utils/agentOutput.ts +173 -0
  183. package/app/utils/catalog.spec.ts +112 -0
  184. package/app/utils/catalog.ts +455 -0
  185. package/app/utils/dnd.ts +29 -0
  186. package/app/utils/observability.ts +52 -0
  187. package/app/utils/pipelineRender.ts +151 -0
  188. package/nuxt.config.ts +55 -0
  189. package/package.json +45 -0
@@ -0,0 +1,71 @@
1
+ // Merge-policy shapes, mirroring `@cat-factory/contracts` (merge.ts). A `merger`
2
+ // agent scores a PR on three 0..1 axes and the engine compares them against the
3
+ // task's resolved threshold preset to auto-merge or raise a review notification.
4
+
5
+ /** A `merger` agent's assessment of a pull request (each axis 0..1). */
6
+ export interface MergeAssessment {
7
+ complexity: number
8
+ risk: number
9
+ impact: number
10
+ rationale: string
11
+ }
12
+
13
+ /**
14
+ * The highest reviewer-finding severity a task tolerates before the requirements review
15
+ * stops for a human. `none` tolerates nothing; `high` tolerates everything. Ordered
16
+ * none < low < medium < high.
17
+ */
18
+ export type RequirementConcernLevel = 'none' | 'low' | 'medium' | 'high'
19
+
20
+ /** A named, per-workspace merge policy a task can select. */
21
+ export interface MergeThresholdPreset {
22
+ id: string
23
+ name: string
24
+ /** Auto-merge only when the assessment's complexity is ≤ this. */
25
+ maxComplexity: number
26
+ /** Auto-merge only when the assessment's risk is ≤ this. */
27
+ maxRisk: number
28
+ /** Auto-merge only when the assessment's impact is ≤ this. */
29
+ maxImpact: number
30
+ /** How many times the CI-fixer may try before the CI gate gives up. */
31
+ ciMaxAttempts: number
32
+ /** How many reviewer passes the requirements-review loop runs before asking the human. */
33
+ maxRequirementIterations: number
34
+ /** Findings at or below this severity auto-pass without stopping for a human. */
35
+ maxRequirementConcernAllowed: RequirementConcernLevel
36
+ /** Minutes the post-release-health gate watches the release's monitors/SLOs after deploy. */
37
+ releaseWatchWindowMinutes: number
38
+ /** How many on-call investigations the post-release-health gate may dispatch. */
39
+ releaseMaxAttempts: number
40
+ /** The workspace's fallback preset, used by tasks that pick none. */
41
+ isDefault: boolean
42
+ createdAt: number
43
+ }
44
+
45
+ /** Create a merge threshold preset. */
46
+ export interface CreateMergePresetInput {
47
+ name: string
48
+ maxComplexity: number
49
+ maxRisk: number
50
+ maxImpact: number
51
+ ciMaxAttempts: number
52
+ maxRequirementIterations: number
53
+ maxRequirementConcernAllowed: RequirementConcernLevel
54
+ releaseWatchWindowMinutes?: number
55
+ releaseMaxAttempts?: number
56
+ isDefault?: boolean
57
+ }
58
+
59
+ /** Patch a merge threshold preset (all fields optional). */
60
+ export interface UpdateMergePresetInput {
61
+ name?: string
62
+ maxComplexity?: number
63
+ maxRisk?: number
64
+ maxImpact?: number
65
+ ciMaxAttempts?: number
66
+ maxRequirementIterations?: number
67
+ maxRequirementConcernAllowed?: RequirementConcernLevel
68
+ releaseWatchWindowMinutes?: number
69
+ releaseMaxAttempts?: number
70
+ isDefault?: boolean
71
+ }
@@ -0,0 +1,157 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Model selection & best-practice prompt fragments. Mirrors the
3
+ // `@cat-factory/contracts` schemas served read-only by the backend.
4
+ // ---------------------------------------------------------------------------
5
+
6
+ import type { AgentKind, BlockType } from './domain'
7
+
8
+ /** Subscription vendors whose pooled tokens drive the Claude Code / Codex harnesses. */
9
+ export type SubscriptionVendor = 'claude' | 'codex' | 'glm' | 'kimi' | 'deepseek'
10
+
11
+ /** Informational list price (per 1M tokens) for a model flavour. */
12
+ export interface ModelCost {
13
+ inputPerMillion: number
14
+ outputPerMillion: number
15
+ currency: string
16
+ }
17
+
18
+ /**
19
+ * A selectable LLM model, resolved to the flavour in use for this deployment
20
+ * (served by `GET /models`). Mirrors `ModelOption` in `@cat-factory/contracts`.
21
+ * The base `flavor`/`provider`/`model` is the always-available fallback
22
+ * (cloudflare/direct), or the subscription itself for subscription-only models.
23
+ * `subscription` (when present) is the alternative the picker prefers once the
24
+ * workspace has a token for its vendor.
25
+ */
26
+ export interface ModelOption {
27
+ id: string
28
+ label: string
29
+ description: string
30
+ flavor: 'cloudflare' | 'direct' | 'subscription'
31
+ /**
32
+ * Whether this model is actually selectable for the workspace: a direct API key for
33
+ * its provider, a connected subscription vendor, or Cloudflare AI enabled. Absent on
34
+ * the deployment-level catalog; present on the per-workspace `/workspaces/:id/models`.
35
+ */
36
+ available?: boolean
37
+ providerLabel: string
38
+ provider: string
39
+ model: string
40
+ vendor?: SubscriptionVendor
41
+ cost?: ModelCost
42
+ contextTokens?: number
43
+ /** True when the effective flavour is flat-rate quota (not budget-metered). */
44
+ quotaBased?: boolean
45
+ /** The alternative subscription flavour for a dual-mode model (GLM/Kimi). */
46
+ subscription?: {
47
+ vendor: SubscriptionVendor
48
+ providerLabel: string
49
+ provider: string
50
+ model: string
51
+ cost?: ModelCost
52
+ contextTokens?: number
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Status of a user's personal (individual-usage) subscription — Claude consumer
58
+ * subscriptions are licensed per individual, so they're stored per-user (not pooled)
59
+ * and unlocked with a personal password. Metadata only; never the token.
60
+ */
61
+ export interface PersonalSubscriptionStatus {
62
+ vendor: SubscriptionVendor
63
+ label: string
64
+ createdAt: number
65
+ updatedAt: number
66
+ lastUsedAt: number | null
67
+ /** Subscription's own expiry (null = no fixed end date). */
68
+ expiresAt: number | null
69
+ /** Whole days until `expiresAt` (negative once lapsed; null when no expiry). */
70
+ expiresInDays: number | null
71
+ expired: boolean
72
+ /** Whether renewal should be surfaced now (expiry within the warning window). */
73
+ renewSoon: boolean
74
+ }
75
+
76
+ /** Connect (or replace) the signed-in user's personal subscription for a vendor. */
77
+ export interface StorePersonalSubscriptionInput {
78
+ vendor: SubscriptionVendor
79
+ label: string
80
+ token: string
81
+ /** Personal password gating the second encryption layer; never stored. */
82
+ password: string
83
+ /** Epoch ms the subscription expires (for renewal warnings); optional. */
84
+ expiresAt?: number | null
85
+ }
86
+
87
+ /** The scope a stored direct-provider API key belongs to. */
88
+ export type ApiKeyScope = 'account' | 'workspace' | 'user'
89
+
90
+ /** The direct providers that own a poolable API key. */
91
+ export type ApiKeyProvider =
92
+ | 'openai'
93
+ | 'anthropic'
94
+ | 'qwen'
95
+ | 'deepseek'
96
+ | 'moonshot'
97
+ | 'openrouter'
98
+ | 'litellm'
99
+
100
+ /** A connected direct-provider API key (metadata + usage), never the secret. */
101
+ export interface ApiKey {
102
+ id: string
103
+ scope: ApiKeyScope
104
+ scopeId: string
105
+ provider: ApiKeyProvider
106
+ label: string
107
+ createdAt: number
108
+ lastUsedAt: number | null
109
+ inputTokens: number
110
+ outputTokens: number
111
+ requestCount: number
112
+ }
113
+
114
+ /** Add a direct-provider API key to a pool. `key` is write-only (the raw secret). */
115
+ export interface AddApiKeyInput {
116
+ provider: ApiKeyProvider
117
+ label: string
118
+ key: string
119
+ }
120
+
121
+ /** A connected subscription credential (metadata + usage), never the secret. */
122
+ export interface VendorCredential {
123
+ id: string
124
+ vendor: SubscriptionVendor
125
+ label: string
126
+ createdAt: number
127
+ lastUsedAt: number | null
128
+ inputTokens: number
129
+ outputTokens: number
130
+ requestCount: number
131
+ }
132
+
133
+ /**
134
+ * A curated best-practice "prompt fragment" served read-only by the backend
135
+ * (`GET /prompt-fragments`). Users pick which apply to a block; the backend folds
136
+ * the selected fragments' bodies into the agent system prompt at run time.
137
+ */
138
+ export interface PromptFragment {
139
+ id: string
140
+ version: string
141
+ title: string
142
+ category: string
143
+ summary: string
144
+ body: string
145
+ appliesTo?: {
146
+ blockTypes?: BlockType[]
147
+ agentKinds?: AgentKind[]
148
+ }
149
+ /** Free-form tags the relevance selector uses (managed/sourced fragments only). */
150
+ tags?: string[]
151
+ /** Provenance when sourced from a repo; absent for built-in/hand-authored. */
152
+ source?: {
153
+ sourceId: string
154
+ path: string
155
+ sha: string
156
+ }
157
+ }
@@ -0,0 +1,74 @@
1
+ // Notification shapes, mirroring `@cat-factory/contracts` (notifications.ts). A
2
+ // notification is a first-class, human-actionable item surfaced on the board that
3
+ // outlives the run that raised it (a PR awaiting a merge decision, a completed
4
+ // pipeline awaiting confirmation, CI that gave up).
5
+
6
+ import type { MergeAssessment } from './merge'
7
+
8
+ export type NotificationType =
9
+ | 'merge_review'
10
+ | 'pipeline_complete'
11
+ | 'ci_failed'
12
+ | 'test_failed'
13
+ | 'requirement_review'
14
+ | 'clarity_review'
15
+ | 'release_regression'
16
+ | 'decision_required'
17
+ export type NotificationStatus = 'open' | 'acted' | 'dismissed'
18
+
19
+ /** The on-call agent's recommendation on a `release_regression`. */
20
+ export type OnCallRecommendation = 'revert' | 'hold' | 'monitor'
21
+
22
+ /** The on-call agent's assessment of a post-release regression. */
23
+ export interface OnCallAssessment {
24
+ culpritConfidence: number
25
+ recommendation: OnCallRecommendation
26
+ rationale: string
27
+ evidence?: string[]
28
+ }
29
+
30
+ /** A regressed monitor/SLO on a `release_regression`. */
31
+ export interface ReleaseSignal {
32
+ kind: 'monitor' | 'slo'
33
+ id: string
34
+ name: string
35
+ state: 'ok' | 'warn' | 'alert' | 'no_data'
36
+ detail?: string
37
+ }
38
+
39
+ /** Optional structured detail for rendering a notification card. */
40
+ export interface NotificationPayload {
41
+ assessment?: MergeAssessment
42
+ prUrl?: string
43
+ pipelineName?: string
44
+ findingCount?: number
45
+ /** The on-call assessment, on a `release_regression`. */
46
+ onCallAssessment?: OnCallAssessment
47
+ /** The regressed monitors/SLOs, on a `release_regression`. */
48
+ releaseSignals?: ReleaseSignal[]
49
+ /** A proposed revert PR URL, when known. */
50
+ revertUrl?: string
51
+ }
52
+
53
+ /** A human-actionable item surfaced on the board. */
54
+ /**
55
+ * Render urgency. A notification starts `normal` (the inbox's usual per-type colour) and
56
+ * is escalated to `urgent` (red) by the backend's sweep once it has waited for a human
57
+ * past the workspace's `waitingEscalationMinutes` threshold. Absent ⇒ `normal`.
58
+ */
59
+ export type NotificationSeverity = 'normal' | 'urgent'
60
+
61
+ export interface Notification {
62
+ id: string
63
+ type: NotificationType
64
+ status: NotificationStatus
65
+ /** Render urgency (yellow vs red); escalated by the backend sweep. Absent ⇒ normal. */
66
+ severity?: NotificationSeverity
67
+ blockId: string | null
68
+ executionId: string | null
69
+ title: string
70
+ body: string
71
+ payload?: NotificationPayload | null
72
+ createdAt: number
73
+ resolvedAt: number | null
74
+ }
@@ -0,0 +1,69 @@
1
+ // Recurring-pipeline shapes, mirroring `@cat-factory/contracts` (recurring.ts). A
2
+ // schedule attaches a pipeline to a service frame and re-runs it on a cadence —
3
+ // run every `intervalHours`, constrained to an optional allowed window (weekdays +
4
+ // an hour-of-day range, in the schedule's timezone). Each schedule owns one reused
5
+ // on-board task block; firing it starts the pipeline against that block.
6
+
7
+ /** Template a schedule was created from; drives the seeded block description. */
8
+ export type ScheduleTemplate = 'dep-update' | 'tech-debt' | 'custom'
9
+
10
+ /** How often a schedule fires and when it is allowed to. */
11
+ export interface Recurrence {
12
+ /** Base cadence in hours (≥1). */
13
+ intervalHours: number
14
+ /** Allowed weekdays (0=Sun..6=Sat). Empty = every day. */
15
+ weekdays: number[]
16
+ /** Inclusive start of the allowed hour-of-day window, or null for no lower bound. */
17
+ windowStartHour: number | null
18
+ /** Exclusive end of the allowed hour-of-day window, or null for no upper bound. */
19
+ windowEndHour: number | null
20
+ /** IANA timezone the weekday/hour window is evaluated in (e.g. 'UTC'). */
21
+ timezone: string
22
+ }
23
+
24
+ /** A recurring pipeline attached to a service. */
25
+ export interface PipelineSchedule {
26
+ id: string
27
+ /** The reused on-board task block the pipeline runs against. */
28
+ blockId: string
29
+ /** The service frame it lives in. */
30
+ frameId: string
31
+ pipelineId: string
32
+ template: ScheduleTemplate
33
+ name: string
34
+ recurrence: Recurrence
35
+ enabled: boolean
36
+ lastRunAt: number | null
37
+ /** Computed epoch-ms of the next eligible fire. */
38
+ nextRunAt: number
39
+ createdAt: number
40
+ }
41
+
42
+ /** One historical fire of a schedule (retained ~1 week), shown in the inspector. */
43
+ export interface ScheduleRun {
44
+ id: string
45
+ scheduleId: string
46
+ executionId: string | null
47
+ status: 'running' | 'done' | 'failed' | 'skipped'
48
+ startedAt: number
49
+ finishedAt: number | null
50
+ outcome: string | null
51
+ }
52
+
53
+ export interface CreateScheduleInput {
54
+ frameId: string
55
+ pipelineId: string
56
+ template?: ScheduleTemplate
57
+ name: string
58
+ recurrence: Recurrence
59
+ enabled?: boolean
60
+ /** The prompt/description for the reused on-board task; empty → the template seed. */
61
+ description?: string
62
+ }
63
+
64
+ export interface UpdateScheduleInput {
65
+ name?: string
66
+ pipelineId?: string
67
+ recurrence?: Recurrence
68
+ enabled?: boolean
69
+ }
@@ -0,0 +1,31 @@
1
+ // Datadog post-release-health settings shapes, mirroring `@cat-factory/contracts`
2
+ // (release.ts). Per-workspace Datadog connection (keys write-only, never read back) and
3
+ // the per-block monitor/SLO mappings the post-release-health gate reads.
4
+
5
+ /** What `GET /datadog/connection` returns — never the secret keys. */
6
+ export interface DatadogConnectionView {
7
+ connected: boolean
8
+ site: string | null
9
+ }
10
+
11
+ /** Set/replace the workspace's Datadog connection. */
12
+ export interface UpsertDatadogConnectionInput {
13
+ site: string
14
+ apiKey: string
15
+ appKey: string
16
+ }
17
+
18
+ /** A block's monitor/SLO mapping for the post-release-health gate. */
19
+ export interface ReleaseHealthConfig {
20
+ blockId: string
21
+ monitorIds: string[]
22
+ sloIds: string[]
23
+ envTag: string | null
24
+ }
25
+
26
+ /** Create/replace a block's release-health config. */
27
+ export interface UpsertReleaseHealthConfigInput {
28
+ monitorIds?: string[]
29
+ sloIds?: string[]
30
+ envTag?: string | null
31
+ }
@@ -0,0 +1,61 @@
1
+ // Requirements-review wire types. Mirror of `@cat-factory/contracts`'
2
+ // requirements.ts, kept in sync by hand like the rest of `~/types/*` (the SPA
3
+ // does not import the backend package directly).
4
+ //
5
+ // A stateless reviewer agent inspects a block's collected requirements and
6
+ // raises questions / gaps / clarifications; a human answers or dismisses each;
7
+ // then the agent folds the answers back into the block's requirements.
8
+
9
+ export type ReviewItemCategory = 'gap' | 'clarification' | 'assumption' | 'risk' | 'question'
10
+
11
+ export type ReviewItemSeverity = 'low' | 'medium' | 'high'
12
+
13
+ export type ReviewItemStatus = 'open' | 'answered' | 'resolved' | 'dismissed'
14
+
15
+ export interface RequirementReviewItem {
16
+ id: string
17
+ category: ReviewItemCategory
18
+ severity: ReviewItemSeverity
19
+ title: string
20
+ detail: string
21
+ status: ReviewItemStatus
22
+ reply: string | null
23
+ createdAt: number
24
+ updatedAt: number
25
+ }
26
+
27
+ /**
28
+ * - `ready`: the reviewer raised findings awaiting human answers/dismissals.
29
+ * - `incorporating`: transient; the driver is folding the answers into a document (the FIRST
30
+ * async stage — the user is back on the board).
31
+ * - `reviewing`: transient; the reviewer is RE-reviewing the folded document (the SECOND
32
+ * async stage). Distinct from `incorporating` so the UI can show which stage is running.
33
+ * - `merged`: the companion produced a document (an internal transient on the async path).
34
+ * - `exceeded`: the iteration cap was hit with findings open — awaiting the human's choice.
35
+ * - `incorporated`: terminal; the requirements phase is settled.
36
+ */
37
+ export type RequirementReviewStatus =
38
+ | 'ready'
39
+ | 'incorporating'
40
+ | 'reviewing'
41
+ | 'merged'
42
+ | 'exceeded'
43
+ | 'incorporated'
44
+
45
+ /** How a human resolves a review that hit its iteration cap. */
46
+ export type ResolveRequirementsExceededChoice = 'extra-round' | 'proceed' | 'stop-reset'
47
+
48
+ export interface RequirementReview {
49
+ id: string
50
+ blockId: string
51
+ status: RequirementReviewStatus
52
+ items: RequirementReviewItem[]
53
+ model: string | null
54
+ incorporatedRequirements: string | null
55
+ /** Reviewer passes run so far (initial review is 1; each re-review adds one). */
56
+ iteration: number
57
+ /** The reviewer-pass budget (from the task's merge preset; an extra round bumps it). */
58
+ maxIterations: number
59
+ createdAt: number
60
+ updatedAt: number
61
+ }
@@ -0,0 +1,27 @@
1
+ // In-org shared services. Mirrors the `@cat-factory/contracts` `services` wire schemas:
2
+ // a `Service` is the account-owned unit of work (a service frame + its subtree + repo),
3
+ // shared across the workspaces that *mount* it; a `WorkspaceMount` places a service onto a
4
+ // workspace board with that board's own frame layout override.
5
+
6
+ export interface Service {
7
+ id: string
8
+ accountId: string | null
9
+ frameBlockId: string
10
+ installationId: number | null
11
+ repoGithubId: number | null
12
+ /** Subdirectory within the linked monorepo this service lives in (null = whole repo). */
13
+ directory?: string | null
14
+ createdAt: number
15
+ /** How many boards mount this service. Set only on the org catalog (for the "Shared" badge). */
16
+ mountCount?: number
17
+ }
18
+
19
+ export interface WorkspaceMount {
20
+ workspaceId: string
21
+ serviceId: string
22
+ /** This board's frame position override. */
23
+ position: { x: number; y: number }
24
+ /** This board's dragged frame size; null/absent = auto-size. */
25
+ size?: { w: number; h: number } | null
26
+ createdAt: number
27
+ }
@@ -0,0 +1,57 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Slack integration. Slack is an extra delivery transport for the existing
3
+ // notification mechanism (merge_review / pipeline_complete / ci_failed), tapping
4
+ // the same NotificationChannel seam server-side. These mirror the
5
+ // `@cat-factory/contracts` Slack schemas so responses drop straight into the store.
6
+ //
7
+ // Two scopes: the connection (+ bot token, never sent here) is per-account; the
8
+ // notification routing is per-workspace; the @-mention member map is per-account.
9
+ // ---------------------------------------------------------------------------
10
+
11
+ import type { NotificationType } from './notifications'
12
+
13
+ /** An account's Slack connection, as exposed to clients — safe metadata only. */
14
+ export interface SlackConnection {
15
+ teamId: string
16
+ teamName: string
17
+ teamIconUrl?: string | null
18
+ botUserId?: string | null
19
+ scopes?: string[]
20
+ connectedAt: number
21
+ }
22
+
23
+ /** Routing for a single notification type: whether it posts, and where. */
24
+ export interface SlackRoute {
25
+ enabled: boolean
26
+ /** A channel id (`C0123…`) or name (`#general`); empty = unrouted. */
27
+ channel: string
28
+ }
29
+
30
+ /** A workspace's Slack notification routing. */
31
+ export interface SlackNotificationSettings {
32
+ routes: Partial<Record<NotificationType, SlackRoute>>
33
+ mentionsEnabled: boolean
34
+ updatedAt: number
35
+ }
36
+
37
+ /** One internal user id → Slack member id mapping entry. */
38
+ export type SlackMemberRole = 'product' | 'engineering'
39
+
40
+ export interface SlackMemberMappingEntry {
41
+ /** Internal user id (`usr_*`) — the same id the member roster shows. */
42
+ userId: string
43
+ slackUserId: string
44
+ /**
45
+ * Notification role: `product` people are @-mentioned on requirement-review
46
+ * findings; everyone else (`engineering`) only when they created the task.
47
+ * Absent means `engineering`.
48
+ */
49
+ role?: SlackMemberRole
50
+ }
51
+
52
+ /** A Slack channel option for the routing picker. */
53
+ export interface SlackChannel {
54
+ id: string
55
+ name: string
56
+ isPrivate: boolean
57
+ }
@@ -0,0 +1,82 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Task-source integration. Individual issues imported from external task
3
+ // trackers (Jira, …) can be attached to a board task as agent context. These
4
+ // mirror the `@cat-factory/contracts` task schemas; the abstraction is
5
+ // source-agnostic, keyed by `source`. Unlike document sources there is no
6
+ // plan/spawn — an issue is linked for context, never expanded into structure.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ import type { CredentialField } from './documents'
10
+
11
+ /** The external task trackers cat-factory can link to. */
12
+ export type TaskSourceKind = 'jira' | 'github'
13
+
14
+ export type { CredentialField }
15
+
16
+ /** A source's self-description: drives the generic connect + import UI. */
17
+ export interface TaskSourceDescriptor {
18
+ source: TaskSourceKind
19
+ label: string
20
+ /** Lucide icon name for the source. */
21
+ icon: string
22
+ credentialFields: CredentialField[]
23
+ refLabel: string
24
+ refPlaceholder: string
25
+ /** Whether the source supports searching its catalogue by title/content. */
26
+ searchable?: boolean
27
+ }
28
+
29
+ /** A workspace's connection to a task source (never carries credentials). */
30
+ export interface TaskConnection {
31
+ source: TaskSourceKind
32
+ /** Human-friendly label for what we're connected to (site URL). */
33
+ label: string
34
+ /** When the connection was established (epoch ms). */
35
+ connectedAt: number
36
+ }
37
+
38
+ /** A single comment on an issue, with its body as lightweight Markdown. */
39
+ export interface TaskComment {
40
+ author: string
41
+ createdAt: string
42
+ body: string
43
+ }
44
+
45
+ /** An issue imported from a source into the workspace, as a structured record. */
46
+ export interface SourceTask {
47
+ source: TaskSourceKind
48
+ /** The source's canonical key for the issue (e.g. `PROJ-123`). */
49
+ externalId: string
50
+ title: string
51
+ url: string
52
+ /** Workflow status name, e.g. `In Progress`. */
53
+ status: string
54
+ /** Issue type name, e.g. `Bug`. */
55
+ type: string
56
+ /** Assignee display name, or null when unassigned. */
57
+ assignee: string | null
58
+ /** Priority name, or null when none. */
59
+ priority: string | null
60
+ labels: string[]
61
+ /** Issue description as lightweight Markdown. */
62
+ description: string
63
+ comments: TaskComment[]
64
+ /** Short plain-text preview of the issue. */
65
+ excerpt: string
66
+ /** The board block this issue is attached to as context, if any. */
67
+ linkedBlockId: string | null
68
+ syncedAt: number
69
+ }
70
+
71
+ /** A lean hit from searching a tracker's issues (not yet imported). */
72
+ export interface TaskSearchResult {
73
+ source: TaskSourceKind
74
+ /** The source's canonical key for the issue (re-usable as an import ref). */
75
+ externalId: string
76
+ title: string
77
+ url: string
78
+ /** Workflow status name, e.g. `In Progress` (may be empty). */
79
+ status: string
80
+ /** Short plain-text preview (may be empty). */
81
+ excerpt: string
82
+ }
@@ -0,0 +1,18 @@
1
+ // Issue-tracker selection shapes, mirroring `@cat-factory/contracts` (tracker.ts).
2
+ // A workspace designates one tracker — GitHub Issues or Jira — where the tech-debt
3
+ // recurring pipeline files its ticket before implementation starts.
4
+
5
+ export type TrackerKind = 'github' | 'jira'
6
+
7
+ export interface TrackerSettings {
8
+ /** The selected tracker, or null when none is configured. */
9
+ tracker: TrackerKind | null
10
+ /** Jira project key new tickets are filed under (e.g. 'ENG'); null unless Jira. */
11
+ jiraProjectKey: string | null
12
+ updatedAt: number
13
+ }
14
+
15
+ export interface PutTrackerSettingsInput {
16
+ tracker: TrackerKind | null
17
+ jiraProjectKey?: string | null
18
+ }