Q176: onboarding progress survives a restart, and an unfinished setup reopens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 05:25:01 -04:00
1 parent 43dcb1e7d3
commit fd79f059ca
20 files changed
+341 -71

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
import type { DatabaseSync } from "node:sqlite";
import type { OnboardingDraft } from "../../shared/commands";
/** Keeps the unfinished wizard so a restart resumes it; null discards it. The draft type has no key field, so no key is stored. */
export function saveOnboardingDraft(db: DatabaseSync, draft: OnboardingDraft | null): void {
db.prepare(`
insert into app_state(id, onboarding_draft_json, updated_at) values (1, ?, ?)
on conflict(id) do update set onboarding_draft_json = excluded.onboarding_draft_json, updated_at = excluded.updated_at
`).run(draft ? JSON.stringify(draft) : null, new Date().toISOString());
}
export function loadOnboardingDraft(db: DatabaseSync): OnboardingDraft | null {
const row = db.prepare("select onboarding_draft_json from app_state where id = 1").get() as { onboarding_draft_json: string | null } | undefined;
return row?.onboarding_draft_json ? JSON.parse(row.onboarding_draft_json) as OnboardingDraft : null;
}
/** Marks the company's setup (the steps after it was created) finished; until then the wizard reopens on it. */
export function completeSetup(db: DatabaseSync, orgId: string): void {
const result = db.prepare("update organizations set setup_completed_at = coalesce(setup_completed_at, ?) where id = ?").run(new Date().toISOString(), orgId);
if (result.changes === 0) throw new Error("Organization was not found.");
console.info(`[onboarding] ${orgId} finished setup`);
}
+3
View File
@@ -1,3 +1,4 @@
import { saveOnboardingDraft } from "./onboarding-progress";
import { COMPANY_PRESETS } from "../../shared/company-presets";
import type { DatabaseSync } from "node:sqlite";
import type {
@@ -32,6 +33,8 @@ export class OnboardingService {
bootstrap(input: BootstrapOnboardingInput): BootstrapOnboardingSummary {
const provider = this.resolveProvider(input.providerKey);
// The company now exists, so the saved wizard is spent; this commits with it.
saveOnboardingDraft(this.deps.db, null);
const orgId = this.organization.createOrganization({
organizationName: input.companyName,
description: input.companyDescription,
+2
View File
@@ -1,3 +1,4 @@
import { loadOnboardingDraft } from "./onboarding-progress";
import type { DatabaseSync, StatementSync } from "node:sqlite";
import type { AppSnapshot } from "../../shared/domain";
import { PROVIDER_CATALOG } from "../../shared/provider-catalog";
@@ -86,6 +87,7 @@ export class SnapshotService {
return {
activeOrgId,
uiPreferences: this.resolveUiPreferences(),
onboardingDraft: loadOnboardingDraft(this.db),
autonomousPaused: this.resolveAutonomousPaused(),
integrationKeys: new IntegrationKeyStore(this.db).statuses(),
organizations,