Skip to content

feat: auto-create a personal org for new users, resume OAuth after org creation - #716

Merged
fclairamb merged 30 commits into
mainfrom
feat/todos-2026-10-30
Oct 31, 2026
Merged

fclairamb merged 30 commits into
mainfrom
feat/todos-2026-10-30

Conversation

@fclairamb

@fclairamb fclairamb commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

What changed

  • When auth.auto_personal_org is on (the SaaS default), a sign-in that would end on /no-org now creates a personal org first (order: pending invitation, email pattern, pending membership request, then auto-create; the user is OWNER). Applies to password/LDAP/passkey login, registration confirmation and provider logins; a second login never creates a second org. Deleting your last org still lands on /no-org.
  • With the same setting on, /no-org hides "I'd rather join an existing organization" unless there is a join reason (?join=<slug>, a pending/rejected request).
  • A pending OAuth/MCP authorization is resumed after an org-less user creates their first org.
  • Confirming a registration now refreshes the user before navigating.

Decided by autopilot

  • The spec was trimmed mid-run (colleague banner, cleanup job and instance setting dropped); the work was redone to the trimmed spec. Its open questions about those features are moot.

Notes for review

  • Gated by the auth.auto_personal_org server setting (SP_AUTH_AUTO_PERSONAL_ORG, also a system parameter applied at startup): on by default in SaaS mode, off otherwise. Off, self-hosted keeps today's /no-org page, join card included. GET /api/v1/config exposes it as autoPersonalOrg. The e2e servers (CI, global-setup.ts, make dev-test) turn it on.
  • Test mode: POST /test/users seeds a soft-deleted personal-org row so seeded users stay org-less; personalOrg: true gives real behaviour.
  • The unreleased migration 031 comment for auto_personal_owner_uid still mentions the removed cleanup/banner.
  • The spec's "an invite that failed" join reason has no /no-org signal today; only ?join=<slug> carries it.

Closes fclairamb/solidping-business#35

fclairamb and others added 2 commits October 30, 2026 10:03
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t org (fclairamb/solidping-business#35)

A new account sent to /login by an MCP client landed on /no-org (it has no
org) and the pending /api/v1/oauth/authorize request was lost: creating the
org opened its dashboard, never the consent screen. resolveHandoffLanding
(web/dash0/src/lib/auth-handoff.ts) and the login page's two /no-org
branches dropped returnTo. They now carry it through orgCreationReturnTo,
which keeps only the authorize request or device approval (unwrapping the
login page a federated sign-in hands back), and CreateOrgCard resumes it
once POST /api/v1/orgs has set the new org's session cookie.
Tests: auth-handoff, login-destination and create-org-card unit tests; the
handoff one fails without the fix.

Closes fclairamb/solidping-business#35

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fclairamb fclairamb added autopilot frozen Batch PR under review: takes only its own fixes, new work goes to the next batch labels Oct 30, 2026
fclairamb and others added 3 commits October 30, 2026 10:05
organizations.auto_personal_owner_uid marks an org created automatically for a
user at their first sign-in; a partial unique index keeps one live personal org
per user. auth.auto_personal_org (SP_AUTH_AUTO_PERSONAL_ORG) defaults to on
under saas, off under self-hosted. Spec 2026-10-30-04.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.69967% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.79%. Comparing base (8344a9d) to head (797134a).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../internal/analytics/analyticstest/analyticstest.go 78.57% 3 Missing ⚠️
server/internal/scrub/scrub.go 86.36% 3 Missing ⚠️
server/internal/analytics/analytics.go 93.54% 2 Missing ⚠️
server/internal/errorreport/errorreport.go 96.55% 1 Missing ⚠️
server/internal/middleware/auth.go 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #716      +/-   ##
==========================================
+ Coverage   89.74%   89.79%   +0.04%     
==========================================
  Files        1297     1302       +5     
  Lines      132288   132534     +246     
==========================================
+ Hits       118728   119005     +277     
+ Misses      13551    13520      -31     
  Partials        9        9              
Files with missing lines Coverage Δ
server/internal/app/sentry_scrub.go 100.00% <100.00%> (+9.09%) ⬆️
server/internal/app/server.go 92.02% <100.00%> (+<0.01%) ⬆️
server/internal/config/config.go 94.09% <100.00%> (+0.03%) ⬆️
server/internal/config/envvars.go 100.00% <100.00%> (ø)
server/internal/db/models/organization.go 100.00% <ø> (ø)
server/internal/db/postgres/personal_orgs.go 100.00% <100.00%> (ø)
server/internal/db/sqlite/personal_orgs.go 100.00% <100.00%> (ø)
server/internal/handlers/auth/join_policy.go 88.08% <100.00%> (+0.70%) ⬆️
server/internal/handlers/auth/personal_org.go 100.00% <100.00%> (ø)
server/internal/handlers/auth/service.go 90.01% <100.00%> (+0.19%) ⬆️
... and 10 more

... and 20 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

fclairamb and others added 4 commits October 30, 2026 10:22
ensurePersonalOrg settles an org-less sign-in in order: a pending invitation
(joined), a matching registration email pattern (joined), a pending membership
request (left on /no-org), else a personal org named like the /no-org
suggestion with the user as OWNER. Wired into every no-org site: password,
LDAP and passkey logins, registration confirmation, federated callbacks and the
session after an org deletion. Gated by auth.auto_personal_org; super admins
and demo users never get one, and a user gets one personal org in a lifetime.

GET /api/v1/auth/colleague-hint names the single org colleagues of a
non-webmail domain use, when it accepts join requests. The daily
personal_org_cleanup job warns the owner of an empty personal org at day 23
and deletes it at day 30 through DeleteOrg. solidping_no_org_outcomes_total
counts landed / auto_created / created / join_requested / left.

Spec 2026-10-30-04.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nnel metrics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s for

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oin trap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fclairamb fclairamb changed the title feat: todos 2026-10-30 feat(onboarding): get new users into an org, and resume MCP authorization after creating it Oct 30, 2026
fclairamb and others added 4 commits October 30, 2026 10:37
… org after confirming

ColleagueHintBanner shows, on the personal org only, the one org colleagues of
the same company domain use, with a request-to-join button (the /no-org
membership-request mutation) and a dismiss that sticks (localStorage).

The confirm-registration page now has the auth context adopt the new session
before navigating: an org landing (personal org, or an auto-joined org) used to
bounce to the login page because the context still held the empty session it
validated on mount.

E2E: auto-personal-org.spec.ts (SaaS: personal org dashboard and the banner;
self-hosted: /no-org). confirm-registration-no-org.spec.ts skips on SaaS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…no-org: create a personal org for them

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fclairamb fclairamb changed the title feat(onboarding): get new users into an org, and resume MCP authorization after creating it feat: auto personal org for new users, resume OAuth after org creation Oct 30, 2026
@fclairamb
fclairamb marked this pull request as ready for review October 30, 2026 10:31
@fclairamb
fclairamb marked this pull request as draft October 30, 2026 10:32
fclairamb and others added 3 commits October 30, 2026 11:46
…e join card without a join reason

Drops the colleague hint, the abandoned-org cleanup, the auth.auto_personal_org
setting and the funnel metric, which the trimmed spec leaves out. Personal org
creation is now unconditional when no invitation, pattern match or pending
request applies; org deletion still lands on /no-org. The /no-org join card is
only rendered with ?join=<slug>, membershipPending or a pending/rejected
request. POST /test/users marks seeded zero-org users so e2e specs stay on
/no-org unless they pass personalOrg: true.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…no-org: create a personal org for them

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fclairamb fclairamb changed the title feat: auto personal org for new users, resume OAuth after org creation feat: auto-create a personal org for new users, resume OAuth after org creation Oct 30, 2026
@fclairamb
fclairamb marked this pull request as ready for review October 30, 2026 11:14
fclairamb and others added 2 commits October 30, 2026 12:16
031_v0_43_0 shipped in v0.42.1; restore it and add the change in 032.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fclairamb and others added 5 commits October 30, 2026 12:25
…an org

A sign-in with no org now joins the pending invitation directly, so the
zero-org variant no longer reaches the invitation page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The slug existing-<timestamp> was 22 characters, so org creation answered 422.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fclairamb and others added 5 commits October 30, 2026 20:57
Stacked on #716: merge it first.

Closes fclairamb/solidping-business#36

## What changed
- Server errors (5xx / `INTERNAL_ERROR` responses, recovered panics in
the HTTP, check-worker and job-worker paths) are now also sent to
PostHog error tracking as `$exception` events when analytics is active.
Sentry is unchanged and independent.
- The dashboard sends its PostHog session and distinct ids to the API
(same-origin only) so a server error links to its replay.
- Redaction is shared with Sentry (new `scrub` package): no secrets, no
request bodies.
- Docs: new "Server errors (error tracking)" section in the analytics
configuration page.

Gate: build, lint, Go tests and dash0 typecheck/lint/unit tests green
(full Playwright not run: server-side and header-only change).

## Decided by autopilot
- None: the issue's decision comment settled the Sentry/PostHog question
(both, independent).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…aaS mode

Personal org creation at sign-in, and hiding the /no-org join card without a
join reason, now sit behind auth.auto_personal_org (SP_AUTH_AUTO_PERSONAL_ORG,
also a system parameter applied at startup). Unset, it follows the deployment
mode: on for saas, off for self-hosted, which keeps today's /no-org page with
its join card. GET /api/v1/config exposes it as autoPersonalOrg for dash0.

The e2e servers (CI, global-setup, make dev-test) turn it on, so the
Playwright specs keep asserting the SaaS behaviour. The tests that needed a
pending membership request to stay org-less are back to their main version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fclairamb
fclairamb enabled auto-merge (squash) October 31, 2026 06:24
@fclairamb
fclairamb merged commit 2ae5575 into main Oct 31, 2026
33 checks passed
@fclairamb
fclairamb deleted the feat/todos-2026-10-30 branch October 31, 2026 06:31
fclairamb added a commit that referenced this pull request Oct 31, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.43.0](v0.42.1...v0.43.0)
(2026-10-31)


### Features

* auto-create a personal org for new users, resume OAuth after org
creation ([#716](#716))
([2ae5575](2ae5575))


### Bug Fixes

* **auth:** limit sign-ups per client IP (unconfirmed per day, once a
minute) ([#727](#727))
([b2eb48f](b2eb48f))
* **deps:** update github.com/dop251/goja digest to 032d024
([#724](#724))
([025e97a](025e97a))
* **deps:** update go dependencies (non-major)
([#725](#725))
([5796b1f](5796b1f))
* **deps:** update module github.com/prometheus/common to v0.73.1
([#720](#720))
([9bef875](9bef875))
* **deps:** update module github.com/slack-go/slack to v0.33.0
([#722](#722))
([ad19df4](ad19df4))
* **security:** bug-report budget for non-members, retention sparing
crawl reports
([#715](#715))
([d929999](d929999))
* **security:** JS check memory budget, re-authentication for new
sign-in factors
([#728](#728))
([a78f954](a78f954))


### Performance Improvements

* **dash0:** load only the active language's translations
([#729](#729))
([406f211](406f211))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge autopilot frozen Batch PR under review: takes only its own fixes, new work goes to the next batch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant