Skip to content

fix(app-builder): repair migrations and stale sessions - #5784

Open
evanjacobson wants to merge 1 commit into
mainfrom
fix/app-builder-migration-bugs
Open

fix(app-builder): repair migrations and stale sessions#5784
evanjacobson wants to merge 1 commit into
mainfrom
fix/app-builder-migration-bugs

Conversation

@evanjacobson

Copy link
Copy Markdown
Contributor

Summary

  • Allow GitHub migration for canonical Kilo user IDs and preserve accurate migration error categories.
  • Recover projects whose Cloud Agent session is missing instead of leaving chat stuck on Building....
  • Add regression coverage for migration failures, canonical session selection, reconnect state, and replacement-session recovery.

Verification

  • App Builder Worker tests: 43 passed
  • Focused web tests: 89 passed
  • App Builder Worker typecheck
  • Changed-file lint and formatting
  • git diff --check
  • Full web typecheck is blocked by unrelated untracked Stripe script errors in the local worktree.

Visual Changes

No layout changes. Missing sessions now show a required fresh-chat state instead of an indefinite Building... state.

Reviewer Notes

Migration implementation
  • Accept non-empty text userId values while retaining UUID validation for orgId.
  • Parse valid Worker failure envelopes before applying HTTP status handling.
  • Expose push_failed only for actual push failures; map validation, token, transport, and internal failures to internal_error.
Session recovery implementation
  • Classify definitive Cloud Agent NOT_FOUND responses as unprepared sessions while leaving transient failures unknown.
  • Use project.session_id as the canonical active session and preserve it as the final session consumed by the UI.
  • Route missing or orphaned sessions through forceNewSession, keep mandatory recovery non-cancelable and retryable, and preserve normal optional New Chat behavior.
  • Do not mark reconnects as streaming until replay supplies busy-state evidence.


export const MigrateToGithubRequestSchema = z.object({
githubRepo: z.string().regex(/^[^/]+\/[^/]+$/, 'Must be in "owner/repo" format'),
userId: z.string().uuid(),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users with non-UUID Kilo User IDs fail due to the parsing here

? sessionInfos.find(s => s.cloud_agent_session_id === project.session_id)
: undefined;

const orderedSessionInfos = activeInfo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

project.session_id is authoritative, but existing chat and feedback consumers treat the final array entry as active. Moving the canonical session to the end keeps those consumers aligned while older/orphaned sessions remain available as history.

}

const effectiveModel = model ?? store.getState().model;
const isRecoveringSession = store.getState().isRecoveringSession;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capture the mode before clearing pendingNewSession: a failed mandatory recovery must remain non-cancelable, while a failed user-requested New Chat must remain optional and cancellable.

: undefined;

if (activeProjectSessionInfo?.initiated === false) {
if (activeProjectSessionInfo?.prepared === true && activeProjectSessionInfo.initiated === false) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deliberately distinguishes a prepared-but-not-started session from a missing session. Only the former can be started in place; missing sessions must use the replacement-session flow.

const data = await response.json().catch(() => undefined);
const parsed = MigrateToGithubResponseSchema.safeParse(data);

if (parsed.success && !parsed.data.success) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worker domain failures intentionally use non-2xx statuses while still returning this typed envelope. Parse that contract before generic HTTP handling so callers retain the actual failure category.

);
sessionInitiated = null;
sessionPrepared = null;
sessionPrepared = isDefinitiveSessionNotFoundError(err) ? false : null;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false means the backend session is definitively absent and enables replacement recovery. Transient Cloud Agent failures stay null so a temporary outage does not abandon an otherwise valid session.

if (!migrateResult.success) {
throw new MigrationError('push_failed', { cause: migrateResult });
throw new MigrationError(
migrateResult.error === 'push_failed' ? 'push_failed' : 'internal_error',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only push_failed means a Git push was actually attempted and failed. Validation, token, transport, and unexpected Worker failures are surfaced as internal_error rather than showing misleading push-failure UI.

@@ -469,7 +469,6 @@ export function createV2StreamingCoordinator(config: V2StreamingConfig): V2Strea
}

logger.log('Connecting to existing V2 session', { sessionId });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A successful transport connection does not mean the agent is working. Replayed busy events now establish streaming state, so a zero-event reconnect cannot leave the UI on Building... indefinitely.

@kilo-code-bot

kilo-code-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (17 files)
  • apps/web/src/components/app-builder/AppBuilderChat.tsx
  • apps/web/src/components/app-builder/ProjectManager.ts
  • apps/web/src/components/app-builder/project-manager/__tests__/ProjectManager.test.ts
  • apps/web/src/components/app-builder/project-manager/__tests__/preview-polling.test.ts
  • apps/web/src/components/app-builder/project-manager/__tests__/store.test.ts
  • apps/web/src/components/app-builder/project-manager/__tests__/v2-streaming.test.ts
  • apps/web/src/components/app-builder/project-manager/sessions/v2/streaming.ts
  • apps/web/src/components/app-builder/project-manager/store.ts
  • apps/web/src/components/app-builder/project-manager/types.ts
  • apps/web/src/lib/app-builder/app-builder-client.test.ts
  • apps/web/src/lib/app-builder/app-builder-client.ts
  • apps/web/src/lib/app-builder/app-builder-service.test.ts
  • apps/web/src/lib/app-builder/app-builder-service.ts
  • apps/web/src/lib/app-builder/github-migration-service.test.ts
  • apps/web/src/lib/app-builder/github-migration-service.ts
  • services/app-builder/src/api-schemas.test.ts
  • services/app-builder/src/api-schemas.ts

Reviewed by grok-4.6 · Input: 222K · Output: 22.5K · Cached: 1.4M

Review guidance: REVIEW.md from base branch main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant