mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 00:03:09 +00:00
Surface task source runtime capability gaps
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -191,6 +191,7 @@ Done means: reviewers and future agents can understand the vision, implementatio
|
||||
- [x] 2026-06-12: Scoped Jira Tasks drawer/list cache reconciliation by Jira task source context and site id so same-key issues from different hosts/accounts cannot replace each other; verified with focused cache selector tests, targeted oxlint, and full typecheck.
|
||||
- [x] 2026-06-12: Seeded New Workspace run-target selection from the incoming task source context so Linear/Jira starts preselect the logical project/source host instead of the ambient active repo; verified with a focused composer host-boundary test, targeted oxlint, and full typecheck.
|
||||
- [x] 2026-06-12: Added explicit disabled-reason copy for external automation actions when an SSH host is disconnected/connecting, another action is in flight, or the local/remote automation source tool is missing; verified with focused availability tests, targeted oxlint, git diff --check, and full typecheck.
|
||||
- [x] 2026-06-12: Added Tasks source-host availability for remote servers that are still loading capabilities or lack task-source-context.v1, so older servers degrade with explicit update/checking copy instead of looking connected-but-empty; verified with focused TaskPage/source-summary tests, targeted oxlint, git diff --check, and full typecheck.
|
||||
- [x] 2026-06-12: Fed Tasks source-context diagnostics from the shared execution-host registry so the source chip now reports SSH availability and blocked/incompatible remote-server hosts; verified with summary tests and web typecheck.
|
||||
- [x] 2026-06-12: Extended Linear/Jira Tasks source diagnostics to include the account-owning host plus blocked remote-server availability, keeping account source separate from workspace run host; verified with summary tests and web typecheck.
|
||||
- [x] 2026-06-12: Started the user-facing Host copy audit by replacing stale “remote project” and “Remote SSH” UI labels with project-on-host / SSH-host language; verified with web typecheck.
|
||||
|
||||
@@ -128,7 +128,10 @@ import PullRequestPage from '@/components/PullRequestPage'
|
||||
import GitLabItemDialog from '@/components/GitLabItemDialog'
|
||||
import ProjectViewWrapper from '@/components/github-project/ProjectViewWrapper'
|
||||
import { getSettingsForRepoRuntimeOwner } from '@/lib/repo-runtime-owner'
|
||||
import { buildExecutionHostRegistry } from '../../../shared/execution-host-registry'
|
||||
import {
|
||||
buildExecutionHostRegistry,
|
||||
type ExecutionHostRegistryEntry
|
||||
} from '../../../shared/execution-host-registry'
|
||||
import { getHostDisplayLabelOverrides } from '../../../shared/host-setting-overrides'
|
||||
import LinearIssueWorkspace from '@/components/LinearIssueWorkspace'
|
||||
import {
|
||||
@@ -152,6 +155,7 @@ import { buildLinearIssueLinkedWorkItem } from '@/lib/linear-linked-work-item'
|
||||
import { isGitRepoKind } from '../../../shared/repo-kind'
|
||||
import { getRepoExecutionHostId } from '../../../shared/execution-host'
|
||||
import { projectHostSetupProjectionFromRepos } from '../../../shared/project-host-setup-projection'
|
||||
import { TASK_SOURCE_CONTEXT_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
|
||||
import {
|
||||
getTaskSourceCacheScope,
|
||||
getTaskSourceRuntimeSettings,
|
||||
@@ -346,6 +350,37 @@ function getTaskPageRepoSourceContext(
|
||||
})
|
||||
}
|
||||
|
||||
function getTaskSourceHostAvailabilityForHost(
|
||||
host: ExecutionHostRegistryEntry | null | undefined,
|
||||
hostId: TaskSourceContext['hostId']
|
||||
): TaskSourceHostAvailability | null {
|
||||
if (!host) {
|
||||
return null
|
||||
}
|
||||
if (host.kind === 'runtime') {
|
||||
if (!host.capabilities) {
|
||||
return {
|
||||
hostId,
|
||||
reason: 'checking-task-source-capability'
|
||||
}
|
||||
}
|
||||
if (!host.capabilities.includes(TASK_SOURCE_CONTEXT_RUNTIME_CAPABILITY)) {
|
||||
return {
|
||||
hostId,
|
||||
reason: 'missing-task-source-capability'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (host.health === 'local' || host.health === 'available') {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
hostId,
|
||||
health: host.health,
|
||||
status: host.connectionStatus
|
||||
}
|
||||
}
|
||||
|
||||
function getTaskPageRepoCacheInput(repo: Repo): {
|
||||
id: string
|
||||
path: string
|
||||
@@ -2896,16 +2931,8 @@ export default function TaskPage(): React.JSX.Element {
|
||||
taskSource === 'github' || taskSource === 'gitlab'
|
||||
? taskSourceRepoContexts.flatMap((context) => {
|
||||
const host = hostRegistryById.get(context.hostId)
|
||||
if (!host || host.health === 'local' || host.health === 'available') {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
hostId: context.hostId,
|
||||
health: host.health,
|
||||
status: host.connectionStatus
|
||||
}
|
||||
]
|
||||
const availability = getTaskSourceHostAvailabilityForHost(host, context.hostId)
|
||||
return availability ? [availability] : []
|
||||
})
|
||||
: [],
|
||||
[hostRegistryById, taskSource, taskSourceRepoContexts]
|
||||
@@ -2972,16 +2999,8 @@ export default function TaskPage(): React.JSX.Element {
|
||||
return []
|
||||
}
|
||||
const host = hostRegistryById.get(accountBackedTaskSourceHostId)
|
||||
if (!host || host.health === 'local' || host.health === 'available') {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
{
|
||||
hostId: host.id,
|
||||
health: host.health,
|
||||
status: host.connectionStatus
|
||||
}
|
||||
]
|
||||
const availability = getTaskSourceHostAvailabilityForHost(host, accountBackedTaskSourceHostId)
|
||||
return availability ? [availability] : []
|
||||
}, [accountBackedTaskSourceHostId, hostRegistryById, taskSource])
|
||||
const taskSourceAvailabilityNoticeByProvider = useMemo<
|
||||
Partial<Record<TaskProvider, TaskSourceAvailabilityNotice>>
|
||||
@@ -2991,22 +3010,15 @@ export default function TaskPage(): React.JSX.Element {
|
||||
): TaskSourceHostAvailability[] =>
|
||||
contexts.flatMap((context) => {
|
||||
const host = hostRegistryById.get(context.hostId)
|
||||
if (!host || host.health === 'local' || host.health === 'available') {
|
||||
return []
|
||||
}
|
||||
return [{ hostId: context.hostId, health: host.health, status: host.connectionStatus }]
|
||||
const availability = getTaskSourceHostAvailabilityForHost(host, context.hostId)
|
||||
return availability ? [availability] : []
|
||||
})
|
||||
const accountHost = hostRegistryById.get(accountBackedTaskSourceHostId)
|
||||
const accountAvailability =
|
||||
!accountHost || accountHost.health === 'local' || accountHost.health === 'available'
|
||||
? []
|
||||
: [
|
||||
{
|
||||
hostId: accountHost.id,
|
||||
health: accountHost.health,
|
||||
status: accountHost.connectionStatus
|
||||
}
|
||||
]
|
||||
const accountHostAvailability = getTaskSourceHostAvailabilityForHost(
|
||||
accountHost,
|
||||
accountBackedTaskSourceHostId
|
||||
)
|
||||
const accountAvailability = accountHostAvailability ? [accountHostAvailability] : []
|
||||
const labelFor = (provider: TaskProvider): string =>
|
||||
sourceOptions.find((source) => source.id === provider)?.label ?? provider
|
||||
return {
|
||||
|
||||
@@ -27,4 +27,16 @@ describe('TaskPage source switching host boundary', () => {
|
||||
expect(section).not.toContain('projectHostSetupId')
|
||||
expect(section).not.toContain('workspaceRunContext')
|
||||
})
|
||||
|
||||
it('treats missing remote task-source capability as source unavailable', () => {
|
||||
const section = sourceBetween(
|
||||
TASK_PAGE_SOURCE,
|
||||
'function getTaskSourceHostAvailabilityForHost',
|
||||
'function getTaskPageRepoCacheInput'
|
||||
)
|
||||
|
||||
expect(section).toContain('TASK_SOURCE_CONTEXT_RUNTIME_CAPABILITY')
|
||||
expect(section).toContain("reason: 'checking-task-source-capability'")
|
||||
expect(section).toContain("reason: 'missing-task-source-capability'")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -177,6 +177,49 @@ describe('task source context summary', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('shows remote-server task-source capability checks', () => {
|
||||
const summary = getTaskSourceContextSummary({
|
||||
provider: 'github',
|
||||
providerLabel: 'GitHub',
|
||||
selectedRepoCount: 1,
|
||||
repoContexts: [
|
||||
{
|
||||
kind: 'task-source',
|
||||
provider: 'github',
|
||||
projectId: 'project-a',
|
||||
hostId: 'runtime:old-server',
|
||||
repoId: 'repo-a',
|
||||
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
|
||||
}
|
||||
],
|
||||
hostAvailability: [
|
||||
{ hostId: 'runtime:old-server', reason: 'checking-task-source-capability' }
|
||||
]
|
||||
})
|
||||
|
||||
expect(summary.label).toBe('GitHub · old-server · checking server capabilities · stablyai/orca')
|
||||
expect(summary.title).toBe(
|
||||
'GitHub · Host: old-server · Availability: old-server checking server capabilities · Source: stablyai/orca'
|
||||
)
|
||||
})
|
||||
|
||||
it('shows remote-server task-source capability version skew', () => {
|
||||
expect(
|
||||
getTaskSourceAvailabilityNotice({
|
||||
providerLabel: 'GitHub',
|
||||
sourceCount: 1,
|
||||
hostAvailability: [
|
||||
{ hostId: 'runtime:old-server', reason: 'missing-task-source-capability' }
|
||||
]
|
||||
})
|
||||
).toEqual({
|
||||
label: 'GitHub source unavailable: old-server server update needed for task sources',
|
||||
title:
|
||||
'Reconnect or update old-server server update needed for task sources to load this source.',
|
||||
blocking: true
|
||||
})
|
||||
})
|
||||
|
||||
it('shows account-backed Linear and Jira sources', () => {
|
||||
expect(
|
||||
getTaskSourceContextSummary({
|
||||
|
||||
@@ -20,6 +20,7 @@ export type TaskSourceHostAvailability = {
|
||||
hostId: ExecutionHostScope
|
||||
status?: SshConnectionStatus
|
||||
health?: ExecutionHostHealth
|
||||
reason?: 'checking-task-source-capability' | 'missing-task-source-capability'
|
||||
}
|
||||
|
||||
export function getTaskSourceContextSummary(args: {
|
||||
@@ -205,6 +206,12 @@ function getUnavailableHosts(hostAvailability: readonly TaskSourceHostAvailabili
|
||||
}
|
||||
|
||||
function getAvailabilityStatusLabel(availability: TaskSourceHostAvailability): string | null {
|
||||
switch (availability.reason) {
|
||||
case 'checking-task-source-capability':
|
||||
return 'checking server capabilities'
|
||||
case 'missing-task-source-capability':
|
||||
return 'server update needed for task sources'
|
||||
}
|
||||
if (availability.status) {
|
||||
return availability.status === 'connected' ? null : getSshStatusLabel(availability.status)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user