feat(byra): gate the automatic cockpit landing to owner/admin (#1970)
* feat(byra): gate the automatic cockpit landing to owner/admin Plain byra members now land like regular users; owner/admin keep the cockpit landing at both decision sites (post-login /api/clients/landing and the '/' bounce). The middleware zero-company steer stays ungated: a member with zero companies has nowhere else to land. Cockpit access itself is unchanged (nav + /clients remain membership-based). Supersedes the 2026-08-05 all-members widening (DECISIONS.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(byra): keep byra members out of the first-run wizard on auto-landing Skeptic finding: a member whose auto-resolved active company is onboarding-incomplete (e.g. mid migration-reset, which repoints active_company_id itself) fell through the new role gate into /onboarding, a dead end for role member (WL-15 refuses client creation). Byra members without a picked-company cookie now go to /byra at the onboarding check, restoring the pre-gate shield. Also pins the role column into the landing route's select assertion so dropping it can't pass the mocked tests silently. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
23776337c8
commit
b30c71086e
@@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
partitionCompaniesByHomeDomain,
|
||||
isCompanyHomedOnHost,
|
||||
isCockpitLandingRole,
|
||||
resolveLandingPath,
|
||||
type TeamBrandRef,
|
||||
} from '../home-domain'
|
||||
@@ -138,6 +139,25 @@ describe('isCompanyHomedOnHost', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isCockpitLandingRole', () => {
|
||||
it('owner gets the automatic cockpit landing', () => {
|
||||
expect(isCockpitLandingRole('owner')).toBe(true)
|
||||
})
|
||||
|
||||
it('admin gets the automatic cockpit landing', () => {
|
||||
expect(isCockpitLandingRole('admin')).toBe(true)
|
||||
})
|
||||
|
||||
it('plain member lands like a regular user', () => {
|
||||
expect(isCockpitLandingRole('member')).toBe(false)
|
||||
})
|
||||
|
||||
it('unknown roles default to the regular landing (allowlist)', () => {
|
||||
expect(isCockpitLandingRole('viewer')).toBe(false)
|
||||
expect(isCockpitLandingRole('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveLandingPath', () => {
|
||||
it("byrå staff on their brand host land in the cockpit", () => {
|
||||
expect(
|
||||
|
||||
@@ -95,11 +95,25 @@ export function isCompanyHomedOnHost(opts: {
|
||||
return opts.companyTeamId === opts.hostBrandTeamId
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a byrå team role gets the AUTOMATIC cockpit landing (2026-08-27:
|
||||
* owner/admin only, superseding the 2026-08-05 all-members widening). Plain
|
||||
* members land like regular users; they can still open the cockpit manually
|
||||
* (nav visibility and access are membership-based, unchanged). Callers drop
|
||||
* non-qualifying memberships BEFORE resolveLandingPath, which stays pure.
|
||||
* Allowlist, not `role !== 'member'`, so any future role defaults to the
|
||||
* regular landing.
|
||||
*/
|
||||
export function isCockpitLandingRole(role: string): boolean {
|
||||
return role === 'owner' || role === 'admin'
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-login landing (WL-14): byrå staff land in the cockpit on the byrå's
|
||||
* home domain: the brand domain when the team has a brand, else the canonical
|
||||
* domain (a byrå team without white label is a valid state, WL-01). Everyone
|
||||
* else keeps today's '/' byte-identically.
|
||||
* else keeps today's '/' byte-identically. Callers pass only memberships that
|
||||
* pass isCockpitLandingRole.
|
||||
*/
|
||||
export function resolveLandingPath(opts: {
|
||||
/** teams.id of the brand serving the current host, null on canonical. */
|
||||
|
||||
@@ -499,8 +499,10 @@ async function updateSessionInner(
|
||||
return supabaseResponse
|
||||
}
|
||||
|
||||
// Byrå team members (any role: widened from owner/admin, founder call
|
||||
// 2026-08-05) with zero client companies (a fresh byrå) home to the
|
||||
// Byrå team members (any role: deliberately NOT gated by
|
||||
// isCockpitLandingRole even after the 2026-08-27 owner/admin landing
|
||||
// gate, because a plain member with zero companies has nowhere else to
|
||||
// land) with zero client companies (a fresh byrå) home to the
|
||||
// EMPTY cockpit, never to the company onboarding wizard: clients are
|
||||
// created from the cockpit, and forcing the wizard here would make a
|
||||
// byrå user create a personal company just to get in. Cockpit-shaped
|
||||
|
||||
Reference in New Issue
Block a user