[eric] workflows: a cloud with no workflows API reads as unavailable with the honest reason, not as the cloud declining the request

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C9zwUaHucUgrdxvK8FvjYT
This commit is contained in:
ciregenz
2026-09-04 08:24:20 -07:00
co-authored by Claude Fable 5.1
parent 4d2cd934b5
commit 425df32fc4
5 changed files with 62 additions and 2 deletions
+10 -1
View File
@@ -49,6 +49,13 @@ class CloudStatusUnknown(CloudStatusBase):
detail: str
# The cloud we are signed into has no workflows API at all (the preflight route 404s): this app is ahead
# of that control plane. Nothing was sent, and the toggle should say so instead of "declined".
class CloudStatusUnavailable(CloudStatusBase):
state: Literal["unavailable"] = "unavailable"
reason: str
class CloudStatusReady(CloudStatusBase):
state: Literal["ready"] = "ready"
plan: Optional[str] = None
@@ -62,7 +69,7 @@ class CloudStatusReady(CloudStatusBase):
credential: CredentialReadiness
CloudStatus = Union[CloudStatusReady, CloudStatusSignedOut, CloudStatusUnknown]
CloudStatus = Union[CloudStatusReady, CloudStatusSignedOut, CloudStatusUnknown, CloudStatusUnavailable]
@typechecked
@@ -124,6 +131,8 @@ async def compute_status(wf: Workflow) -> CloudStatus:
except cloud.CloudUnreachable as exc:
return CloudStatusUnknown(detail=exc.detail, **shared)
except cloud.CloudRefused as exc:
if exc.status == 404:
return CloudStatusUnavailable(reason="Cloud runs are not available on your OpenSwarm Cloud yet; this version of the app is ahead of it. Nothing was sent.", **shared)
return CloudStatusUnknown(detail=exc.message, **shared)
p_mirror_cloud_state(wf, pre.hosted)
@@ -0,0 +1,28 @@
"""A 404 on the cloud's preflight route means the signed-in cloud has no workflows API: this app is
ahead of it. That is a blocked state with the honest words, not "The cloud declined this request"."""
import asyncio
from backend.apps.workflows.cloud import client as cloud
from backend.apps.workflows.cloud import status as status_mod
from backend.apps.workflows.models import Workflow
def p_wf() -> Workflow:
return Workflow(name="probe", steps=[])
def test_a_404_preflight_is_unavailable_and_says_nothing_was_sent(monkeypatch):
async def refuse(definition, hosted_id=None):
raise cloud.CloudRefused("The cloud declined this request.", 404)
monkeypatch.setattr(cloud, "preflight", refuse)
st = asyncio.run(status_mod.compute_status(p_wf()))
assert st.state == "unavailable" and "not available on your OpenSwarm Cloud yet" in st.reason and "Nothing was sent" in st.reason
def test_any_other_refusal_stays_unknown_with_the_clouds_words(monkeypatch):
async def refuse(definition, hosted_id=None):
raise cloud.CloudRefused("Your plan does not include cloud runs.", 403)
monkeypatch.setattr(cloud, "preflight", refuse)
st = asyncio.run(status_mod.compute_status(p_wf()))
assert st.state == "unknown" and st.detail == "Your plan does not include cloud runs."
@@ -62,7 +62,13 @@ export interface CloudStatusUnknown extends CloudStatusShared {
detail: string;
}
export type CloudStatus = CloudStatusReady | CloudStatusSignedOut | CloudStatusUnknown;
// The signed-in cloud has no workflows API (its preflight route 404s): this app is ahead of it.
export interface CloudStatusUnavailable extends CloudStatusShared {
state: 'unavailable';
reason: string;
}
export type CloudStatus = CloudStatusReady | CloudStatusSignedOut | CloudStatusUnknown | CloudStatusUnavailable;
export interface CloudRun {
id: string;
@@ -0,0 +1,16 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { cloudAvailability } from './cloudAvailability';
import type { CloudProbe } from './cloudApi';
// A cloud with no workflows API used to read as "The cloud declined this request" under an unknown
// state (Haik, 2026-09-03: "requests routed to Cloud don't go through"). It is a blocked state with a
// reason that says the app is ahead of the cloud and nothing was sent.
test('a cloud without the workflows API blocks the toggle with the honest reason', () => {
const probe = { phase: 'answered', status: { state: 'unavailable', reason: 'Cloud runs are not available on your OpenSwarm Cloud yet; this version of the app is ahead of it. Nothing was sent.', target: 'device', schedule_supported: true, schedule_reason: null } } as unknown as CloudProbe;
const a = cloudAvailability(probe);
assert.equal(a.kind, 'blocked');
assert.match((a as { reason: string }).reason, /not available on your OpenSwarm Cloud yet/);
assert.equal((a as { action: string | null }).action, null);
});
@@ -43,6 +43,7 @@ export function cloudAvailability(probe: CloudProbe): CloudAvailability {
return { kind: 'blocked', reason: status.schedule_reason, action: null };
}
if (status.state === 'unknown') return { kind: 'unknown', detail: status.detail };
if (status.state === 'unavailable') return { kind: 'blocked', reason: status.reason, action: null };
if (status.state === 'signed_out') {
return {
kind: 'blocked',