From fc12e30c142ffb81b042642da48e452a0410a900 Mon Sep 17 00:00:00 2001 From: burn Date: Wed, 29 Jul 2026 15:14:27 -0400 Subject: [PATCH 1/2] fix: clarify degraded integration recovery Session-Id: ded1b209-9eee-4893-9e62-15b85847e24f --- .../relayfile-integration-preflight.test.ts | 22 +++++++++++++++++++ src/mount/relayfile-integration-preflight.ts | 7 ++++++ 2 files changed, 29 insertions(+) diff --git a/src/mount/relayfile-integration-preflight.test.ts b/src/mount/relayfile-integration-preflight.test.ts index eb66a3d6..63c0a6a7 100644 --- a/src/mount/relayfile-integration-preflight.test.ts +++ b/src/mount/relayfile-integration-preflight.test.ts @@ -131,4 +131,26 @@ describe('Relayfile integration preflight', () => { expect(terminal.confirm).not.toHaveBeenCalled() expect(relayfile.connect).not.toHaveBeenCalled() }) + + it('directs completed-but-degraded integrations to an authorized repair instead of waiting', async () => { + const relayfile = connections(async () => ({ + ready: false, + state: 'degraded', + initialSyncState: 'complete', + })) + const terminal = io() + + await expect(ensureFactoryIntegrations({ + connections: relayfile, + providers: ['github'], + workspaceId: 'rw_test', + interactive: false, + dryRun: true, + io: terminal, + })).rejects.toThrow( + /degraded after its initial sync completed \(degraded, complete\).*workspace owner.*repair or reconnect github/u, + ) + expect(relayfile.connect).not.toHaveBeenCalled() + expect(relayfile.waitForConnection).not.toHaveBeenCalled() + }) }) diff --git a/src/mount/relayfile-integration-preflight.ts b/src/mount/relayfile-integration-preflight.ts index e3ad5c33..c52af375 100644 --- a/src/mount/relayfile-integration-preflight.ts +++ b/src/mount/relayfile-integration-preflight.ts @@ -125,6 +125,13 @@ const notReadyMessage = ( const details = [observation.state, observation.initialSyncState] .filter((value): value is string => Boolean(value)) .join(', ') + const state = observation.state?.trim().toLowerCase() + const initialSyncState = observation.initialSyncState?.trim().toLowerCase() + if (state === 'degraded' && initialSyncState === 'complete') { + return `[factory] ${provider} is connected but degraded after its initial sync completed` + + `${details ? ` (${details})` : ''}; ask an Agent Relay workspace owner to repair or reconnect ` + + `${provider}, then retry.` + } return `[factory] ${provider} is connected but not ready${details ? ` (${details})` : ''}; wait for its initial sync, then retry.` } From 3f7df08a91227477f9bba3d6c712a39db66ef524 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Sat, 22 Aug 2026 18:07:29 +0200 Subject: [PATCH 2/2] test(mount): cover the degraded-state normalization path The existing degraded case fed canonical lowercase values, so the `.trim().toLowerCase()` normalization in `notReadyMessage` was not load-bearing for any assertion -- deleting it kept the suite green. Add a mixed-case, whitespace-padded case. Verified by mutation: with the normalization stripped, only this test fails, and it fails because the provider falls back to "wait for its initial sync" -- exactly the advice #203 exists to stop. The assertion echoes the raw reported values in the details string rather than normalized ones, because that is what `notReadyMessage` builds: normalization selects the branch, the text quotes the provider verbatim so an operator can match it against the dashboard. Raised by CodeRabbit on 5f1c2a4. Co-Authored-By: Claude Opus 5 Session-Id: ded1b209-9eee-4893-9e62-15b85847e24f --- .../relayfile-integration-preflight.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/mount/relayfile-integration-preflight.test.ts b/src/mount/relayfile-integration-preflight.test.ts index 63c0a6a7..d72d626b 100644 --- a/src/mount/relayfile-integration-preflight.test.ts +++ b/src/mount/relayfile-integration-preflight.test.ts @@ -153,4 +153,31 @@ describe('Relayfile integration preflight', () => { expect(relayfile.connect).not.toHaveBeenCalled() expect(relayfile.waitForConnection).not.toHaveBeenCalled() }) + + it('classifies a degraded completed integration by its normalized state, echoing the reported values', async () => { + const relayfile = connections(async () => ({ + ready: false, + state: ' DeGraded ', + initialSyncState: '\tComplete ', + })) + const terminal = io() + + // Only `.trim().toLowerCase()` routes this to the repair branch; without it + // the provider falls back to "wait for its initial sync", which is the + // advice #203 is about. The details string deliberately echoes the values + // as the provider reported them, so the operator can match them to what + // the dashboard shows -- normalization decides the branch, not the text. + await expect(ensureFactoryIntegrations({ + connections: relayfile, + providers: ['github'], + workspaceId: 'rw_test', + interactive: false, + dryRun: true, + io: terminal, + })).rejects.toThrow( + /degraded after its initial sync completed \( DeGraded ,\s+Complete \).*workspace owner.*repair or reconnect github/u, + ) + expect(relayfile.connect).not.toHaveBeenCalled() + expect(relayfile.waitForConnection).not.toHaveBeenCalled() + }) })