Skip to content

Commit 44bac37

Browse files
MattBroclaude
andauthored
fix: skip non-essential health checks for signup and show clear errors on outages (#413)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bbd07f9 commit 44bac37

6 files changed

Lines changed: 102 additions & 17 deletions

File tree

bin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ function runWizard(
510510

511511
await tui.store.runReadyHooks();
512512
await tui.store.getGate('intro');
513+
await tui.store.getGate('health-check');
513514

514515
const skipAgent = config.run == null;
515516

src/lib/agent/agent-runner.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ import { getCloudUrlFromRegion } from '../../utils/urls';
3737
import {
3838
evaluateWizardReadiness,
3939
WizardReadiness,
40+
SIGNUP_WIZARD_READINESS_CONFIG,
41+
getBlockingServiceKeys,
42+
SERVICE_LABELS,
4043
} from '../health-checks/readiness';
4144
import { enableDebugLogs, initLogFile, logToFile } from '../../utils/debug';
4245
import { createBenchmarkPipeline } from '../middleware/benchmark';
@@ -178,10 +181,29 @@ export async function runWorkflow(
178181
// 2. Health check (guarded — skip if TUI already ran it)
179182
if (!session.readinessResult) {
180183
logToFile('[agent-runner] evaluating wizard readiness');
181-
const readiness = await evaluateWizardReadiness();
184+
const readinessConfig = session.signup
185+
? SIGNUP_WIZARD_READINESS_CONFIG
186+
: undefined;
187+
const readiness = await evaluateWizardReadiness(readinessConfig);
182188
logToFile(`[agent-runner] readiness=${readiness.decision}`);
183189
if (readiness.decision === WizardReadiness.No) {
190+
const blockingKeys = getBlockingServiceKeys(
191+
readiness.health,
192+
readinessConfig,
193+
);
194+
const blockingLabels = blockingKeys.map(
195+
(k) => `${SERVICE_LABELS[k]} (${readiness.health[k].status})`,
196+
);
197+
logToFile(`[agent-runner] blocked by: ${blockingLabels.join(', ')}`);
198+
184199
await getUI().showBlockingOutage(readiness);
200+
201+
await wizardAbort({
202+
message:
203+
'Cannot start — external services are down:\n' +
204+
blockingLabels.map((l) => ` - ${l}`).join('\n') +
205+
'\n\nPlease try again later.',
206+
});
185207
} else if (readiness.decision === WizardReadiness.YesWithWarnings) {
186208
getUI().setReadinessWarnings(readiness);
187209
}

src/lib/health-checks/readiness.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ export const DEFAULT_WIZARD_READINESS_CONFIG: WizardReadinessConfig = {
6868
degradedBlocksRun: ['anthropic'],
6969
};
7070

71+
/**
72+
* Reduced readiness config for --signup provisioning flows.
73+
*
74+
* Provisioning only needs PostHog and the LLM Gateway - it doesn't
75+
* use Anthropic directly, npm, GitHub Releases, or MCP.
76+
*/
77+
export const SIGNUP_WIZARD_READINESS_CONFIG: WizardReadinessConfig = {
78+
downBlocksRun: ['posthogOverall', 'llmGateway'],
79+
};
80+
7181
// ---------------------------------------------------------------------------
7282
// Aggregate check
7383
// ---------------------------------------------------------------------------

src/lib/workflows/posthog-integration/steps.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { RunPhase } from '../../wizard-session.js';
1212
import {
1313
evaluateWizardReadiness,
1414
WizardReadiness,
15+
SIGNUP_WIZARD_READINESS_CONFIG,
16+
getBlockingServiceKeys,
1517
} from '../../health-checks/readiness.js';
1618
import { detectPostHogIntegration } from './detect.js';
1719

@@ -26,8 +28,22 @@ function needsSetup(session: WizardSession): boolean {
2628

2729
function healthCheckReady(session: WizardSession): boolean {
2830
if (!session.readinessResult) return false;
29-
if (session.readinessResult.decision === WizardReadiness.No)
31+
32+
if (session.signup) {
33+
const hardBlocking = getBlockingServiceKeys(
34+
session.readinessResult.health,
35+
SIGNUP_WIZARD_READINESS_CONFIG,
36+
);
37+
const defaultBlocking = getBlockingServiceKeys(
38+
session.readinessResult.health,
39+
);
40+
if (hardBlocking.length === 0 && defaultBlocking.length === 0) return true;
41+
return session.outageDismissed;
42+
}
43+
44+
if (session.readinessResult.decision === WizardReadiness.No) {
3045
return session.outageDismissed;
46+
}
3147
return true;
3248
}
3349

src/ui/logging-ui.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
import { TaskStatus, type WizardUI, type SpinnerHandle } from './wizard-ui';
88
import type { SettingsConflict } from '../lib/agent/agent-interface';
9-
import type { WizardReadinessResult } from '../lib/health-checks/readiness.js';
9+
import {
10+
type WizardReadinessResult,
11+
getBlockingServiceKeys,
12+
SERVICE_LABELS,
13+
} from '../lib/health-checks/readiness.js';
1014
import type { OutroData } from '../lib/wizard-session';
1115

1216
export class LoggingUI implements WizardUI {
@@ -91,12 +95,23 @@ export class LoggingUI implements WizardUI {
9195

9296
showBlockingOutage(result: WizardReadinessResult): Promise<void> {
9397
console.log(`▲ Service health issues detected — blocking outage.`);
98+
const blockingKeys = getBlockingServiceKeys(result.health);
99+
if (blockingKeys.length > 0) {
100+
console.log(`│`);
101+
console.log(`│ Blocking services:`);
102+
for (const key of blockingKeys) {
103+
const status = result.health[key].status;
104+
const error = result.health[key].error;
105+
const label = SERVICE_LABELS[key];
106+
const detail = error ? ` — ${error}` : '';
107+
console.log(`│ ✖ ${label}: ${status}${detail}`);
108+
}
109+
console.log(`│`);
110+
}
94111
for (const reason of result.reasons) {
95112
console.log(`│ ${reason}`);
96113
}
97-
console.log(
98-
`│ The wizard may not work reliably while services are affected.`,
99-
);
114+
console.log(`│ The wizard cannot start while these services are down.`);
100115
return Promise.resolve();
101116
}
102117

src/ui/tui/screens/health/HealthCheckScreen.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import {
1717
} from '../../primitives/index.js';
1818
import { Colors, Icons } from '../../styles.js';
1919
import { ServiceHealthList } from '../../components/ServiceHealthList.js';
20-
import { getBlockingServiceKeys } from '../../../../lib/health-checks/readiness.js';
20+
import {
21+
getBlockingServiceKeys,
22+
SIGNUP_WIZARD_READINESS_CONFIG,
23+
} from '../../../../lib/health-checks/readiness.js';
2124
import { ServiceHealthStatus } from '../../../../lib/health-checks/types.js';
2225
import { wizardAbort } from '../../../../utils/wizard-abort.js';
2326
import { fetchSkillMenu, downloadSkill } from '../../../../lib/wizard-tools.js';
@@ -86,22 +89,41 @@ export const HealthCheckScreen = ({ store }: HealthCheckScreenProps) => {
8689
);
8790
}
8891

89-
// Healthy or warnings — isComplete returns true, router skips past.
90-
// This branch only renders for a single frame before advancing.
91-
const blockingKeys = getBlockingServiceKeys(result.health);
92-
if (blockingKeys.length === 0) return null;
92+
const isSignup = store.session.signup;
93+
const blockingKeys = getBlockingServiceKeys(
94+
result.health,
95+
isSignup ? SIGNUP_WIZARD_READINESS_CONFIG : undefined,
96+
);
9397

94-
const isGithubReleasesDown = blockingKeys.includes('githubReleases');
98+
// Signup has a narrower block list (only posthog + llm-gateway), so
99+
// services like Anthropic can be degraded without blocking. Surface
100+
// those as dismissable warnings instead of silently proceeding.
101+
const warningKeys = isSignup
102+
? getBlockingServiceKeys(result.health).filter(
103+
(k) => !blockingKeys.includes(k),
104+
)
105+
: [];
106+
107+
const hasHardBlock = blockingKeys.length > 0;
108+
const displayKeys = hasHardBlock ? blockingKeys : warningKeys;
109+
if (displayKeys.length === 0) return null;
110+
111+
const isGithubReleasesDown =
112+
hasHardBlock && blockingKeys.includes('githubReleases');
95113
const canDownloadSkills =
96114
result.health.githubReleases.status === ServiceHealthStatus.Healthy;
97115
const integration = store.session.integration;
98116

99-
const title = `Ongoing service disruptions`;
117+
const title = hasHardBlock
118+
? 'Ongoing service disruptions'
119+
: 'Service disruption detected';
100120

101121
const docsUrl = store.session.frameworkConfig?.metadata.docsUrl;
102122
const description = isGithubReleasesDown
103123
? "The Wizard can't download necessary skills from GitHub Releases right now."
104-
: 'The Wizard may not work reliably while services are affected.';
124+
: hasHardBlock
125+
? 'The Wizard cannot start while these services are down.'
126+
: 'Some services are degraded. You can continue, but parts of the wizard may not work reliably.';
105127

106128
const handleDownloadAndExit = async () => {
107129
if (downloading) return;
@@ -131,10 +153,9 @@ export const HealthCheckScreen = ({ store }: HealthCheckScreenProps) => {
131153
: 'Download skills & Exit [Esc]'
132154
: 'Exit [Esc]';
133155

134-
// Blocking outage — show service list with Continue/Exit
135156
return (
136157
<ModalOverlay
137-
borderColor="red"
158+
borderColor={hasHardBlock ? 'red' : 'yellow'}
138159
title={title}
139160
width={72}
140161
footer={
@@ -173,7 +194,7 @@ export const HealthCheckScreen = ({ store }: HealthCheckScreenProps) => {
173194

174195
<ServiceHealthList
175196
health={result.health}
176-
filterKeys={blockingKeys}
197+
filterKeys={displayKeys}
177198
showHealthy={false}
178199
/>
179200
</Box>

0 commit comments

Comments
 (0)