Skip to content

feat(console): wire onboarding to real /orgs + eERC onboarding + treasury#63

Merged
hitakshiA merged 1 commit into
mainfrom
codex/62-onboarding
Jul 11, 2026
Merged

feat(console): wire onboarding to real /orgs + eERC onboarding + treasury#63
hitakshiA merged 1 commit into
mainfrom
codex/62-onboarding

Conversation

@hitakshiA

@hitakshiA hitakshiA commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Epic: #58 · Step C (Onboarding) · Closes #62

Rewires the console's onboarding wizard off its demo-era endpoints onto the real eERC backend, building on the cookie-SIWE foundation (#60). A signed-in user with no org can now: create an org → run eERC onboarding for their address → provision the managed treasury → land in the workspace with that org active.

What changed

  • lib/api.ts — real client fns matching the backend: createOrg({name,slug})POST /orgs; startOnboarding(mockKyc?)POST /onboarding/start; onboardingStatus()GET /onboarding/status; subscribeOnboardingStatus() — a credentialed EventSource on /onboarding/status/stream with a 2s polling fallback and clean teardown, closing on complete/failed; provisionTreasury(orgId)POST /orgs/:id/treasury {consent:true}. Removed the dead demo-era onboarding/treasury-provision fns + paths.
  • app/Onboarding.tsx Wizard — driven entirely by the real calls: Business step → createOrg (slug derived, persists ACTIVE_ORG_KEY = org.id); a live eERC progress view streamed from onboarding.steps (KYC → allowlist → gas → registration); Treasury step → provisionTreasury (shows the treasury address + registration tx); finish → store refresh() + land in workspace.
  • app/RootGate.tsx — a signed-in user with no activeOrg is routed into the wizard; on finish it refreshes and (org now active) renders the Shell.
  • packages/typesOnboardingStatus (+ steps) and org/treasury-provision response types matching the backend.
  • Demo — stubs updated to the new shapes; VITE_DEMO_MODE=1 still walks the wizard.

Out of scope (later epic steps)

Treasury screen (D), payroll (E), hiding no-backend screens (B).

Verification (all green)

typecheck · test (72) · build · VITE_DEMO_MODE=1 build · biome lint .

Summary by CodeRabbit

  • New Features

    • Added wallet sign-in and organization creation to the onboarding experience.
    • Introduced guided eERC onboarding with live progress updates, transaction details, and error handling.
    • Added managed treasury provisioning with registration status and transaction visibility.
    • Users without an active organization are now routed through onboarding before entering the workspace.
    • Demo mode now simulates the complete onboarding and treasury provisioning flow.
  • Bug Fixes

    • Added fallback status polling when live onboarding updates are unavailable.

Greptile Summary

This PR wires console onboarding to the real org, eERC, and treasury APIs. The main changes are:

  • Org creation now posts to /orgs and stores the active org id.
  • eERC onboarding starts through /onboarding/start and renders live step progress.
  • Treasury provisioning now posts consent to /orgs/:id/treasury.
  • RootGate routes signed-in users without an active org into onboarding.
  • Demo data, API stubs, tests, and shared API types were updated to the new shapes.

Confidence Score: 4/5

The onboarding stream handling needs a fix before merging.

  • The main org creation, treasury provisioning, and RootGate flow are consistent with the new API shape.
  • The status stream can miss valid default SSE messages and leave onboarding stuck.
  • Polling only starts after stream errors, so a healthy stream with ignored messages does not recover.

apps/console/src/lib/api.ts

Important Files Changed

Filename Overview
apps/console/src/app/Onboarding.tsx Replaces demo KYB and treasury steps with real org creation, eERC progress, treasury provisioning, and final refresh.
apps/console/src/app/RootGate.tsx Adds routing for signed-in users who need to create their first active org.
apps/console/src/lib/api.ts Adds real onboarding API calls and an SSE subscription with polling fallback.
packages/types/src/api.ts Adds shared request and response types for org creation, eERC onboarding, and treasury provisioning.
apps/console/src/demo/api.ts Updates demo API behavior to match the new onboarding and treasury flow.
apps/console/src/demo/data.ts Adds demo onboarding status and treasury provision state.
apps/console/src/lib/api.test.ts Updates API tests for the new endpoints and credentialed status streaming.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant User
  participant Console
  participant API
  participant Stream
  User->>Console: Complete business step
  Console->>API: POST /orgs
  API-->>Console: Created org
  Console->>API: POST /onboarding/start
  API-->>Console: Initial onboarding status
  Console->>Stream: GET /onboarding/status/stream
  Stream-->>Console: Onboarding progress events
  Console->>API: POST /orgs/:id/treasury
  API-->>Console: Treasury provisioned
  Console->>API: Refresh session and orgs
  Console-->>User: Workspace shell
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant User
  participant Console
  participant API
  participant Stream
  User->>Console: Complete business step
  Console->>API: POST /orgs
  API-->>Console: Created org
  Console->>API: POST /onboarding/start
  API-->>Console: Initial onboarding status
  Console->>Stream: GET /onboarding/status/stream
  Stream-->>Console: Onboarding progress events
  Console->>API: POST /orgs/:id/treasury
  API-->>Console: Treasury provisioned
  Console->>API: Refresh session and orgs
  Console-->>User: Workspace shell
Loading

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/console/src/lib/api.ts:321
**Default SSE Messages Are Ignored**

When `/onboarding/status/stream` sends standard SSE messages without a custom `event: status` field, this listener never receives them and the healthy connection does not trigger polling fallback. The wizard then stays in the eERC onboarding step even though the backend is publishing progress.

Reviews (1): Last reviewed commit: "feat(console): wire onboarding to real /..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

…sury

Rewire the onboarding wizard off the demo-era endpoints onto the backend:
create the org (POST /orgs), run eERC onboarding for the signed-in address
(POST /onboarding/start + a credentialed status stream with a polling
fallback), and provision the managed treasury (POST /orgs/:id/treasury). On
finish the store refreshes and the now-active org lands in the workspace.
RootGate routes a signed-in user with no active org into the wizard. Demo
stubs updated to the new shapes.

Epic #58 step C. Closes #62.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The console onboarding flow now creates an organization, starts eERC onboarding with streamed or polled progress, provisions a managed treasury, and routes authenticated sessions without an active organization through the wizard before entering the workspace.

Changes

Organization onboarding

Layer / File(s) Summary
Onboarding and treasury contracts
packages/types/src/api.ts
Adds DTOs and endpoint mappings for organization creation, eERC onboarding status, status streaming, and treasury provisioning.
API operations and status delivery
apps/console/src/lib/api.ts, apps/console/src/lib/api.test.ts
Replaces legacy onboarding helpers with organization-scoped operations, EventSource streaming, polling fallback, and endpoint coverage.
Demo onboarding simulation
apps/console/src/demo/*
Adds seeded onboarding and treasury state plus timed demo status progression and provisioning behavior.
Wizard onboarding and treasury UI
apps/console/src/app/Onboarding.tsx
Updates the wizard to create organizations, display eERC progress, provision treasuries, and show registration details.
No-active-organization routing
apps/console/src/app/RootGate.tsx
Routes authenticated sessions without an active organization into onboarding and refreshes state after completion.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OnboardingWizard
  participant API
  participant Backend
  User->>OnboardingWizard: Submit organization details
  OnboardingWizard->>API: Create organization
  API->>Backend: POST /orgs
  Backend-->>API: Organization response
  OnboardingWizard->>API: Start eERC onboarding
  API->>Backend: POST /onboarding/start
  Backend-->>API: Status updates
  API-->>OnboardingWizard: Stream or poll onboarding status
  OnboardingWizard->>API: Provision treasury
  API->>Backend: POST /orgs/{orgId}/treasury
  Backend-->>API: Treasury registration response
  OnboardingWizard-->>User: Show completion and enter workspace
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR matches the linked issue by adding real org creation, eERC onboarding streaming/polling, treasury provisioning, active-org routing, and demo-mode support.
Out of Scope Changes check ✅ Passed The changes stay focused on onboarding, API/types, demo support, and routing, with no clear unrelated feature work.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: wiring console onboarding to real org creation, eERC onboarding, and treasury provisioning.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/62-onboarding

Comment @coderabbitai help to get the list of available commands.

try {
source = new EventSource(apiHref("/onboarding/status/stream"), { withCredentials: true });
source.addEventListener("status", (event) => {
try {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Default SSE Messages Are Ignored

When /onboarding/status/stream sends standard SSE messages without a custom event: status field, this listener never receives them and the healthy connection does not trigger polling fallback. The wizard then stays in the eERC onboarding step even though the backend is publishing progress.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/console/src/lib/api.ts
Line: 321

Comment:
**Default SSE Messages Are Ignored**

When `/onboarding/status/stream` sends standard SSE messages without a custom `event: status` field, this listener never receives them and the healthy connection does not trigger polling fallback. The wizard then stays in the eERC onboarding step even though the backend is publishing progress.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex

@hitakshiA

Copy link
Copy Markdown
Contributor Author

Greptile (default SSE messages ignored): by design — the backend's /onboarding/status/stream emits named events only (event: status and event: terminal), and the subscription handles both, plus an onerror → 2s-polling fallback and a terminal-status auto-close. Unnamed default message events don't occur on this stream, so there's nothing to catch there. If the contract ever adds default messages we'd add an onmessage handler, but it'd be dead code today.

@hitakshiA
hitakshiA merged commit 4f0ad8a into main Jul 11, 2026
3 checks passed
@hitakshiA
hitakshiA deleted the codex/62-onboarding branch July 11, 2026 03:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/console/src/app/Onboarding.tsx`:
- Around line 171-175: Update the canNext logic in Onboarding to require both a
treasury address and successful registration and consent status before allowing
the treasury step to advance; use the relevant treasury response fields
(registered and consented) alongside treasury?.address, while preserving the
existing org and KYB checks.

In `@apps/console/src/app/RootGate.tsx`:
- Around line 29-35: Update the useEffect in RootGate to explicitly set
orgOnboarding to false whenever session.activeOrg exists, while preserving the
existing false reset when there is no session; ensure the flag is assigned on
both branches so a refreshed active organization cannot leave stale onboarding
state.

In `@apps/console/src/lib/api.test.ts`:
- Around line 23-24: Update the test fixture’s chain configuration to use the
shared Fuji metadata from `@benzo/config`: import the Fuji chain object and shared
chain ID, replace the hardcoded chainId with the shared value, and derive
chainEnv from the imported chain object instead of duplicating "testnet".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a58bfe04-1ca7-4043-86a3-d2ede73eeb36

📥 Commits

Reviewing files that changed from the base of the PR and between c9634ca and 9410ec3.

📒 Files selected for processing (7)
  • apps/console/src/app/Onboarding.tsx
  • apps/console/src/app/RootGate.tsx
  • apps/console/src/demo/api.ts
  • apps/console/src/demo/data.ts
  • apps/console/src/lib/api.test.ts
  • apps/console/src/lib/api.ts
  • packages/types/src/api.ts

Comment on lines 171 to 175
const canNext =
step.key === "org" ? !!draft.name && !!draft.legalName :
step.key === "kyb" ? kyb?.status === "approved" :
step.key === "treasury" ? !!mvk :
step.key === "org" ? !!draft.name?.trim() :
step.key === "kyb" ? onboardingComplete :
step.key === "treasury" ? !!treasury?.address :
true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require registration and consent before finishing onboarding.

An address alone permits advancing even when the response says registered: false or consented: false; users can then enter the workspace despite the treasury UI reporting registration pending.

Proposed fix
-    step.key === "treasury" ? !!treasury?.address :
+    step.key === "treasury" ? !!treasury?.address && treasury.registered && treasury.consented :
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const canNext =
step.key === "org" ? !!draft.name && !!draft.legalName :
step.key === "kyb" ? kyb?.status === "approved" :
step.key === "treasury" ? !!mvk :
step.key === "org" ? !!draft.name?.trim() :
step.key === "kyb" ? onboardingComplete :
step.key === "treasury" ? !!treasury?.address :
true;
const canNext =
step.key === "org" ? !!draft.name?.trim() :
step.key === "kyb" ? onboardingComplete :
step.key === "treasury" ? !!treasury?.address && treasury.registered && treasury.consented :
true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/app/Onboarding.tsx` around lines 171 - 175, Update the
canNext logic in Onboarding to require both a treasury address and successful
registration and consent status before allowing the treasury step to advance;
use the relevant treasury response fields (registered and consented) alongside
treasury?.address, while preserving the existing org and KYB checks.

Comment on lines +29 to +35
useEffect(() => {
if (!session) {
setOrgOnboarding(false);
return;
}
if (!session.activeOrg) setOrgOnboarding(true);
}, [session]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset orgOnboarding when an active organization appears.

This effect only changes the flag to true. If a refresh later supplies session.activeOrg, the stale flag still routes the user to onboarding because Line 45 checks orgOnboarding first.

Proposed fix
 useEffect(() => {
-  if (!session) {
-    setOrgOnboarding(false);
-    return;
-  }
-  if (!session.activeOrg) setOrgOnboarding(true);
+  setOrgOnboarding(Boolean(session && !session.activeOrg));
 }, [session]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!session) {
setOrgOnboarding(false);
return;
}
if (!session.activeOrg) setOrgOnboarding(true);
}, [session]);
useEffect(() => {
setOrgOnboarding(Boolean(session && !session.activeOrg));
}, [session]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/app/RootGate.tsx` around lines 29 - 35, Update the useEffect
in RootGate to explicitly set orgOnboarding to false whenever session.activeOrg
exists, while preserving the existing false reset when there is no session;
ensure the flag is assigned on both branches so a refreshed active organization
cannot leave stale onboarding state.

Comment on lines +23 to +24
chainEnv: "testnet",
chainId: 43113,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant files =="
git ls-files | rg '^(apps/console/src/lib/api\.test\.ts|packages/.*/config|.*config.*)$' || true

echo
echo "== search for `@benzo/config` usage =="
rg -n "`@benzo/config`|chainEnv|chainId|testnet|43113" apps/console/src packages -g '!**/node_modules/**' || true

echo
echo "== map api.test.ts =="
if [ -f apps/console/src/lib/api.test.ts ]; then
  wc -l apps/console/src/lib/api.test.ts
  cat -n apps/console/src/lib/api.test.ts
fi

echo
echo "== map candidate config files =="
for f in $(git ls-files | rg '^.*config.*\.(ts|tsx|js|mjs|cjs|json)$' | head -n 40); do
  echo "--- $f"
  wc -l "$f"
done

Repository: Miny-Labs/benzo-console

Length of output: 25688


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file list =="
git ls-files | rg '^(apps/console/src/lib/api\.test\.ts|.*`@benzo/config`.*|.*config.*)$' || true

echo
echo "== api.test.ts =="
sed -n '1,200p' apps/console/src/lib/api.test.ts

echo
echo "== `@benzo/config` references =="
rg -n "`@benzo/config`|chainEnv|chainId|testnet|43113" apps/console/src packages -g '!**/node_modules/**' || true

Repository: Miny-Labs/benzo-console

Length of output: 15752


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== packages/config/src/index.ts (relevant ranges) =="
sed -n '1,120p' packages/config/src/index.ts
echo
sed -n '120,240p' packages/config/src/index.ts
echo
sed -n '240,320p' packages/config/src/index.ts

echo
echo "== packages/config/test/index.test.ts =="
sed -n '1,120p' packages/config/test/index.test.ts

echo
echo "== apps/console/src/lib/api.ts =="
sed -n '1,220p' apps/console/src/lib/api.ts

Repository: Miny-Labs/benzo-console

Length of output: 17830


Import the shared Fuji chain metadata here. chainEnv/chainId still duplicate the test chain setup; pull the chain ID from @benzo/config and derive the env label from the shared chain object so this fixture stays in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/lib/api.test.ts` around lines 23 - 24, Update the test
fixture’s chain configuration to use the shared Fuji metadata from
`@benzo/config`: import the Fuji chain object and shared chain ID, replace the
hardcoded chainId with the shared value, and derive chainEnv from the imported
chain object instead of duplicating "testnet".

Source: Coding guidelines

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.

feat(console): onboarding onto real /orgs + /onboarding + treasury provision

1 participant