Skip to content
Merged
15 changes: 15 additions & 0 deletions .changeset/21849-retire-sys-account-link-social.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/platform-objects': minor
---

fix(platform-objects)!: retire the `sys_account` `link_social` action, which was dead on every boot; `unlink_account` stays

Clause-②: no (narrowing)

<!-- adr-0087: not-required (no-migration-prescription) The withdrawn action is platform-shipped metadata on sys_account, not something an author writes: no spec key, spelling, export name or config field is retired or renamed, and nothing an author wrote needs rewriting. No stored shape carries it either: sys_account is lock full, so no sys_metadata overlay of the object can be saved, and an action is not a stored row. No reachable member of an exported type carries it, measured on the built declarations: SysAccount's declared type keeps only fields from its literal, so its action names already resolved to string, and the bundles are typed as TranslationData objects with no key types. -->

**BREAKING**: `sys_account` no longer declares the `link_social` action, so the "Link Social Account" toolbar button is gone from the Account app's Linked Accounts list and from Setup's Identity Links. It never completed a link on any boot: it navigated to a `GET` of the social sign-in route, which is served as `POST` only, and it offered a fixed list of seven providers whatever the boot had configured. It is retired under ADR-0049 (enforce or remove) and ships as `minor` under the launch-window convention for narrowings.

**What stays.** `unlink_account` is unchanged: the same type, target, placement and row-id parameter. Its confirm question no longer says the user can re-link "from their account settings", because no console surface offers that now. The `sys_account._actions.link_social` leaves are gone from the `en`, `zh-CN`, `ja-JP` and `es-ES` bundles.

**What to do after upgrading.** Linking a social or OIDC identity stays available through the signed-in `POST /api/v1/auth/link-social`, which is `auth.accounts.linkSocial({ provider, callbackURL })` in `@objectstack/client`: call it and navigate to the `url` it answers.
11 changes: 8 additions & 3 deletions content/docs/protocol/objectui/actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,15 @@ label: View Dashboard
type: url
target: /_console/dashboards/sales_overview

name: link_social
label: Connect Account
name: open_in_maps
label: Open in Maps
type: url
target: '/api/v1/auth/sign-in/social?provider=${param.provider}&callbackURL=${ctx.origin}/_console/apps/account/sys_account'
target: 'https://www.google.com/maps/search/?api=1&query=${param.address}'
params:
- name: address
label: Address
type: text
required: true
```

For a **static** url, `openIn: 'new-tab'` opens `target` in a new tab and `openIn: 'self'` navigates in place; omitted, absolute URLs open in a new tab and relative ones navigate in place. For an **async handler that redirects**, set `opensInNewTab: true` instead (the renderer pre-opens the tab synchronously so popup blockers don't fire); `newTabUrl` provides a zero-roundtrip new-tab target template supporting the `{recordId}` placeholder.
Expand Down
1 change: 0 additions & 1 deletion packages/platform-objects/src/apps/account.app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
* make the experience equivalent to the old account SPA for supported
* surfaces:
* - `sys_oauth_application.create` — one-time client_secret reveal
* - `sys_account.link_social` — OAuth redirect URL
*
* The same objects also appear (admin-only) in `setup.app.ts`'s
* Advanced group, gated by `manage_platform_settings`, for tenant-wide
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,9 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
}
},
_actions: {
link_social: {
label: "Link Social Account",
params: {
provider: {
label: "Provider",
options: {
google: "Google",
github: "GitHub",
microsoft: "Microsoft",
apple: "Apple",
facebook: "Facebook",
gitlab: "GitLab",
discord: "Discord"
}
}
}
},
unlink_account: {
label: "Unlink Account",
description: "Unlink this identity link? The user will no longer be able to sign in with this provider until they re-link it from their account settings.",
description: "Unlink this identity link? The user will no longer be able to sign in with this provider until they re-link it.",
successMessage: "Identity link removed"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,9 @@ export const esESObjects: NonNullable<TranslationData['objects']> = {
}
},
_actions: {
link_social: {
label: "Vincular cuenta social",
params: {
provider: {
label: "Proveedor",
options: {
google: "Google",
github: "GitHub",
microsoft: "Microsoft",
apple: "Apple",
facebook: "Facebook",
gitlab: "GitLab",
discord: "Discord"
}
}
}
},
unlink_account: {
label: "Desvincular cuenta",
description: "¿Desvincular este vínculo de identidad? El usuario ya no podrá iniciar sesión con este proveedor hasta que lo vuelva a vincular desde la configuración de su cuenta.",
description: "¿Desvincular este vínculo de identidad? El usuario ya no podrá iniciar sesión con este proveedor hasta que lo vuelva a vincular.",
successMessage: "Vínculo de identidad eliminado"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
*/

export const esESGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_account._actions.link_social.params.provider.options.apple": "cfdc41e15ed6699b",
"objects.sys_account._actions.link_social.params.provider.options.discord": "12f931cc062e76ae",
"objects.sys_account._actions.link_social.params.provider.options.facebook": "7eea009178f5b807",
"objects.sys_account._actions.link_social.params.provider.options.github": "2971d247eb4e6abc",
"objects.sys_account._actions.link_social.params.provider.options.gitlab": "bc3dbbf4b650e600",
"objects.sys_account._actions.link_social.params.provider.options.google": "6fadcd05bb8da367",
"objects.sys_account._actions.link_social.params.provider.options.microsoft": "17309efbdb1ec122",
"objects.sys_email.fields.cc_addresses.label": "02946d952cf15623",
"objects.sys_email.fields.error.label": "786fed84bd8d5a32",
"objects.sys_email.fields.message_id.label": "14cd089a4062f4b1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,9 @@ export const jaJPObjects: NonNullable<TranslationData['objects']> = {
}
},
_actions: {
link_social: {
label: "ソーシャルアカウント連携",
params: {
provider: {
label: "プロバイダー",
options: {
google: "Google",
github: "GitHub",
microsoft: "Microsoft",
apple: "Apple",
facebook: "Facebook",
gitlab: "GitLab",
discord: "Discord"
}
}
}
},
unlink_account: {
label: "連携解除",
description: "このID連携を解除しますか?ユーザーがアカウント設定から再度連携するまで、このプロバイダーでサインインできなくなります。",
description: "このID連携を解除しますか?ユーザーが再度連携するまで、このプロバイダーでサインインできなくなります。",
successMessage: "アイデンティティ連携を解除しました"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
*/

export const jaJPGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_account._actions.link_social.params.provider.options.apple": "cfdc41e15ed6699b",
"objects.sys_account._actions.link_social.params.provider.options.discord": "12f931cc062e76ae",
"objects.sys_account._actions.link_social.params.provider.options.facebook": "7eea009178f5b807",
"objects.sys_account._actions.link_social.params.provider.options.github": "2971d247eb4e6abc",
"objects.sys_account._actions.link_social.params.provider.options.gitlab": "bc3dbbf4b650e600",
"objects.sys_account._actions.link_social.params.provider.options.google": "6fadcd05bb8da367",
"objects.sys_account._actions.link_social.params.provider.options.microsoft": "17309efbdb1ec122",
"objects.sys_email.fields.bcc_addresses.label": "8674899b7b6d5126",
"objects.sys_email.fields.cc_addresses.label": "02946d952cf15623",
"objects.sys_email.fields.message_id.label": "14cd089a4062f4b1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
// table holds nothing BEYOND the echoes is not asserted, for the reason the
// next section gives.
//
// Since that walk, the seven `sys_account._actions.link_social` provider-brand
// rows (Google through Discord) left this ledger together with the action,
// retired under ADR-0049 enforce-or-remove (#21849): the bundle no longer
// carries those leaves, so the ledger below holds 47 echoes.
//
// ## What this file deliberately does NOT assert
//
// That no OTHER es-ES objects leaf reads its `en` source. A new field's label
Expand Down Expand Up @@ -89,8 +94,6 @@ interface Decision {
departure?: string;
}

const BRAND =
'A sign-in provider brand, shown as the name a user recognises on the link button. This bundle renders no brand name: the authored SSO empty state keeps Okta, Entra and Auth0 verbatim.';
const BARE_ID =
'The bare initialism ID. This bundle keeps ID verbatim inside every label that carries it (ID de usuario, ID de organización, ID de cliente); a bare en ID has no noun to render, and adding one would invent content.';
const JWKS =
Expand Down Expand Up @@ -151,15 +154,7 @@ const DECISIONS: readonly Decision[] = [
reason: 'The team record page field zh-CN reproduced as MEMBER COUNT: an ordinary field label an admin reads, an unauthored fill.',
},

// ── Kept as written by design (54) ───────────────────────────────────────
echo('sys_account._actions.link_social.params.provider.options.google', 'Google', BRAND),
echo('sys_account._actions.link_social.params.provider.options.github', 'GitHub', BRAND),
echo('sys_account._actions.link_social.params.provider.options.microsoft', 'Microsoft', BRAND),
echo('sys_account._actions.link_social.params.provider.options.apple', 'Apple', BRAND),
echo('sys_account._actions.link_social.params.provider.options.facebook', 'Facebook', BRAND),
echo('sys_account._actions.link_social.params.provider.options.gitlab', 'GitLab', BRAND),
echo('sys_account._actions.link_social.params.provider.options.discord', 'Discord', BRAND),

// ── Kept as written by design (47) ───────────────────────────────────────
echo('sys_oauth_application.fields.jwks.label', 'JWKS', JWKS),
echo('sys_oauth_application.fields.jwks_uri.label', 'JWKS URI', JWKS),
echo('sys_oauth_application._actions.create_oauth_application.params.type.options.web', 'Web', WEB_CLIENT_TYPE),
Expand Down Expand Up @@ -242,10 +237,10 @@ function undeclaredEchoes(rows: readonly Decision[]): string[] {
}

describe('#20493 es-ES — the ledger itself (controls before verdicts)', () => {
it('is the size it claims: 3 pinned translations and 54 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(57);
it('is the size it claims: 3 pinned translations and 47 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(50);
expect(DECISIONS.filter((d) => d.verdict === 'translate').length).toBe(3);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(54);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(47);
expect(new Set(DECISIONS.map((d) => d.path)).size).toBe(DECISIONS.length);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
// table holds nothing BEYOND the echoes is not asserted, for the reason the
// next section gives.
//
// Since that walk, the seven `sys_account._actions.link_social` provider-brand
// rows (Google through Discord) left this ledger together with the action,
// retired under ADR-0049 enforce-or-remove (#21849): the bundle no longer
// carries those leaves, so the ledger below holds 36 echoes.
//
// ## What this file deliberately does NOT assert
//
// That no OTHER ja-JP objects leaf reads its `en` source. A new field's label
Expand Down Expand Up @@ -89,8 +94,6 @@ interface Decision {
departure?: string;
}

const BRAND =
'A sign-in provider brand, shown as the name a user recognises on the link button. This bundle renders no brand name in kana: the authored SSO empty state keeps Okta, Entra and Auth0 verbatim.';
const BARE_ID =
'The bare initialism ID. This bundle keeps ID verbatim inside every label that carries it (ユーザー ID, 組織 ID, クライアント ID); a bare en ID has no noun to render, and adding one would invent content.';
const JWKS =
Expand Down Expand Up @@ -143,15 +146,7 @@ const DECISIONS: readonly Decision[] = [
reason: 'The team record page field zh-CN reproduced as MEMBER COUNT: an ordinary field label an admin reads, an unauthored fill.',
},

// ── Kept in English by design (43) ───────────────────────────────────────
echo('sys_account._actions.link_social.params.provider.options.google', 'Google', BRAND),
echo('sys_account._actions.link_social.params.provider.options.github', 'GitHub', BRAND),
echo('sys_account._actions.link_social.params.provider.options.microsoft', 'Microsoft', BRAND),
echo('sys_account._actions.link_social.params.provider.options.apple', 'Apple', BRAND),
echo('sys_account._actions.link_social.params.provider.options.facebook', 'Facebook', BRAND),
echo('sys_account._actions.link_social.params.provider.options.gitlab', 'GitLab', BRAND),
echo('sys_account._actions.link_social.params.provider.options.discord', 'Discord', BRAND),

// ── Kept in English by design (36) ───────────────────────────────────────
echo('sys_oauth_application.fields.jwks.label', 'JWKS', JWKS),
echo('sys_oauth_application.fields.jwks_uri.label', 'JWKS URI', JWKS),
echo('sys_oauth_application._actions.create_oauth_application.params.type.options.web', 'Web', WEB_CLIENT_TYPE),
Expand Down Expand Up @@ -221,10 +216,10 @@ function undeclaredEchoes(rows: readonly Decision[]): string[] {
}

describe('#20493 ja-JP — the ledger itself (controls before verdicts)', () => {
it('is the size it claims: 3 pinned translations and 43 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(46);
it('is the size it claims: 3 pinned translations and 36 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(39);
expect(DECISIONS.filter((d) => d.verdict === 'translate').length).toBe(3);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(43);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(36);
expect(new Set(DECISIONS.map((d) => d.path)).size).toBe(DECISIONS.length);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
// table holds nothing BEYOND the echoes is not asserted, for the reason the
// next section gives.
//
// Since that walk, the seven `sys_account._actions.link_social` provider-brand
// rows (Google through Discord) left this ledger together with the action,
// retired under ADR-0049 enforce-or-remove (#21849): the bundle no longer
// carries those leaves, so the ledger below holds 35 echoes.
//
// ## What this file deliberately does NOT assert
//
// That no OTHER zh-CN objects leaf reads its `en` source. A new field's label
Expand Down Expand Up @@ -80,8 +85,6 @@ interface Decision {
departure?: string;
}

const BRAND =
'A sign-in provider brand, shown as the name a user recognises on the link button. The bundle renders no brand name: the authored SSO empty state keeps Okta, Entra and Auth0 verbatim.';
const BARE_ID =
'The bare initialism ID. This bundle keeps ID verbatim inside every label that carries it (用户 ID, 团队 ID, 客户端 ID); a bare en ID has no noun to render, and adding one would invent content.';
const JWKS =
Expand Down Expand Up @@ -132,15 +135,7 @@ const DECISIONS: readonly Decision[] = [
reason: 'Reproduced on the team record page as MEMBER COUNT: an ordinary field label an admin reads, an unauthored fill.',
},

// ── Kept in English by design (42) ───────────────────────────────────────
echo('sys_account._actions.link_social.params.provider.options.google', 'Google', BRAND),
echo('sys_account._actions.link_social.params.provider.options.github', 'GitHub', BRAND),
echo('sys_account._actions.link_social.params.provider.options.microsoft', 'Microsoft', BRAND),
echo('sys_account._actions.link_social.params.provider.options.apple', 'Apple', BRAND),
echo('sys_account._actions.link_social.params.provider.options.facebook', 'Facebook', BRAND),
echo('sys_account._actions.link_social.params.provider.options.gitlab', 'GitLab', BRAND),
echo('sys_account._actions.link_social.params.provider.options.discord', 'Discord', BRAND),

// ── Kept in English by design (35) ───────────────────────────────────────
echo('sys_oauth_application.fields.jwks.label', 'JWKS', JWKS),
echo('sys_oauth_application.fields.jwks_uri.label', 'JWKS URI', JWKS),
echo(
Expand Down Expand Up @@ -217,10 +212,10 @@ function undeclaredEchoes(rows: readonly Decision[]): string[] {
}

describe('#20462 — the ledger itself (controls before verdicts)', () => {
it('is the size it claims: 3 pinned translations and 42 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(45);
it('is the size it claims: 3 pinned translations and 35 declared echoes, no path twice', () => {
expect(DECISIONS.length).toBe(38);
expect(DECISIONS.filter((d) => d.verdict === 'translate').length).toBe(3);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(42);
expect(DECISIONS.filter((d) => d.verdict === 'echo').length).toBe(35);
expect(new Set(DECISIONS.map((d) => d.path)).size).toBe(DECISIONS.length);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,9 @@ export const zhCNObjects: NonNullable<TranslationData['objects']> = {
}
},
_actions: {
link_social: {
label: "关联社交账号",
params: {
provider: {
label: "服务提供商",
options: {
google: "Google",
github: "GitHub",
microsoft: "Microsoft",
apple: "Apple",
facebook: "Facebook",
gitlab: "GitLab",
discord: "Discord"
}
}
}
},
unlink_account: {
label: "解除关联",
description: "确定要解除此身份关联吗?在用户从账户设置中重新关联之前,将无法再使用此提供方登录。",
description: "确定要解除此身份关联吗?在用户重新关联之前,将无法再使用此提供方登录。",
successMessage: "已解除身份关联"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@
*/

export const zhCNGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_account._actions.link_social.params.provider.options.apple": "cfdc41e15ed6699b",
"objects.sys_account._actions.link_social.params.provider.options.discord": "12f931cc062e76ae",
"objects.sys_account._actions.link_social.params.provider.options.facebook": "7eea009178f5b807",
"objects.sys_account._actions.link_social.params.provider.options.github": "2971d247eb4e6abc",
"objects.sys_account._actions.link_social.params.provider.options.gitlab": "bc3dbbf4b650e600",
"objects.sys_account._actions.link_social.params.provider.options.google": "6fadcd05bb8da367",
"objects.sys_account._actions.link_social.params.provider.options.microsoft": "17309efbdb1ec122",
"objects.sys_email.fields.message_id.label": "14cd089a4062f4b1",
"objects.sys_email_template.fields.id.label": "00b0385c9c152888",
"objects.sys_metadata.fields.id.label": "00b0385c9c152888",
Expand Down
Loading
Loading