fix(platform-objects)!: retire the sys_account link_social action, dead on every boot; unlink_account stays - #21894
Conversation
The action navigated to a GET of the POST-only social sign-in route with a fixed provider list, so it was dead on every boot. It is removed under ADR-0049 enforce-or-remove; unlink_account stays. Comments, the spec docblock, the docs example and the checklist item stop citing it, the echo-decision ledgers drop its seven provider rows, and the unlink confirm question no longer points at a re-link affordance in account settings. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
…k_social retirement node scripts/check-i18n-bundles.mjs --write --filter=platform-objects. The diff removes the sys_account._actions.link_social leaves from the four objects bundles and their seven provenance rows from each of the three source-hash tables, and rewrites the en unlink confirm question from source. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
…ged unlink action sys_account declares exactly unlink_account and no action targeting a social sign-in or link door; unlink_account keeps its type, target, placement and row-id param; the four objects bundles and three provenance tables carry no sys_account._actions.link_social leaf. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
Clause-② no (narrowing); ADR-0087 disposition not-required (no-migration-prescription). Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
…cial retirement Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
…hat holds it The domain:devx seat objected on the card: the checklist item is held by the claim on a sibling checklist card, whose entry adds the item's link fixture through POST /api/v1/auth/link-social. The checklist half leaves this PR and is revised from main once that card lands. The file is restored to origin/main byte for byte. Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7fd8189f043a3c005cb03c3ddd9560d7a6a65349 && git checkout 7fd8189f043a3c005cb03c3ddd9560d7a6a65349
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 67c544cccab757d5c27296a4265246c7e3dfffc0 c2bea2c7897fdd03e09eb56ee362cdabbb2ce502 && git checkout -B drift-repro 67c544cccab757d5c27296a4265246c7e3dfffc0 && git merge --no-ff c2bea2c7897fdd03e09eb56ee362cdabbb2ce502
node scripts/docs-audit/affected-docs.mjs --json 67c544cccab757d5c27296a4265246c7e3dfffc0
|
ACCEPT (seat review) — PR #21894 at head
|
Contract reviewServed-tier: Inputs read: card #21849 (body and all 14 comments), PR #21894 (body, 17-file list, net diff against ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Adopted by
Generated by Claude Code |
Fixes #21849
Clause-②: no (narrowing)
Retires the
sys_accountlink_socialaction from@objectstack/platform-objectsunder ADR-0049 enforce-or-remove, as ruled on the card (ruling D, comment 5995717941; maintainer reply verbatim: 「同意」).unlink_accountstays. Self-service linking returns as a native console surface reading/auth/configonce a linking need is named; that is not this PR.Why
link_socialwas atype: 'url'toolbar action. It navigated to a GET of better-auth's social sign-in route, which better-auth serves as POST only, and it offered a fixed list of seven providers whatever the boot had configured, with no visibility gate. It was dead on every boot: the earlier measurement on the card (5992085207) read 404 on a provider-less boot and on a configured one. The ruled direction (options from the configured providers, hidden when none) cannot be said in today's action contract, so the ruling retires the action instead of widening four lanes for one consumer.Linking stays reachable through the signed-in
POST /api/v1/auth/link-social, which isauth.accounts.linkSocialin@objectstack/client. That door, itsplugin-authroute-ledger rows and the SDK method are untouched.What changed
packages/platform-objects/src/identity/sys-account.object.ts: the action and the comment paragraph that introduced it are gone. A short comment now records why there is no link action and where linking lives.unlink_accountkeeps its name, type, target, mode, placement and row-id param. Its confirm question drops "from their account settings"; see Deviation from the claim below.node scripts/check-i18n-bundles.mjs --write --filter=platform-objects). The diff removes only thesys_account._actions.link_socialblock from each of the four objects bundles (17 lines each) and its seven provenance rows from each of the three source-hash tables, and rewrites theenunlink question from source. The three translated unlink questions are hand-written values, edited by hand to match.link_socialprovider-brand rows and the now-unused brand reason. The size pins move with them: zh-CN 45 to 38 rows (42 to 35 echoes), ja-JP 46 to 39 (43 to 36), es-ES 57 to 50 (54 to 47). A header note in each says why.packages/platform-objects/src/identity/sys-account-link-social-retired.test.ts.sys-member.object.ts(theadd_membericon note now cites the Account app's Linked Accounts entry, which uses the same icon) andapps/account.app.ts(the resultDialog list).packages/spec/src/ui/action.zod.ts: thetargetdocblock sentence that citedlink_socialand its dead GET target is removed. Docblock only, no schema line; the interpolation and encoding sentences stay.content/docs/protocol/objectui/actions.mdx: the URL Actions example no longer teacheslink_social. It is replaced by a working${param.X}example (a Maps search URL with anaddressparam); the interpolation prose stays..changeset/21849-retire-sys-account-link-social.md:@objectstack/platform-objects: minor, BREAKING,Clause-②: no (narrowing), ADR-0087not-required (no-migration-prescription). That category fits because the withdrawn action is platform-shipped metadata on alock: 'full'object: no spec key, spelling, export name or config field is retired, nothing an author wrote needs rewriting, and no stored row can carry it. No@objectstack/specentry: its change is one docblock sentence, and no gate asked for one.The checklist half left this PR
The
domain:devxseat objected on the card (5996996202):identity-auth.linked-accounts-socialindocs/qa/platform-checklist/areas/identity-auth.jsonis held by the claim on #21851, whose entry adds the item's link fixture throughPOST /api/v1/auth/link-social. #21851 had not landed when this PR opened, so the checklist half is not here. The file is restored toorigin/mainbyte for byte:git diff origin/main -- docs/qa/platform-checklist/areas/identity-auth.jsonis empty, and the blob at HEAD equals the one onorigin/mainand at the base (f0734d3cb7). The seat files that revision as its own card when this lands, to be worked once #21851 has landed. Until then the item's link step still names the retired action.Deviation from the claim
The claim said
unlink_accountstays byte-identical. Its confirm question told the user they could re-link "from their account settings", and after this retirement no console surface offers a link (zero hits for a link affordance in objectui at the pin, see H4). The dev contract says a shipped text that this change makes false is fixed in the same change, so the clause is dropped in all four locales. Name, type, target, mode, placement and params are unchanged and pinned. Reverting that one sentence is a single-file edit plus a regeneration, if the seat prefers the claim's reading.Census
H1, every reference (base
e864db56df, outsideCHANGELOG.md).sys-account.object.ts: the action (about :58-84) and its intro comment (:51-56)sys-member.object.ts:137apps/account.app.ts:22*.objects.generated.ts*.source-hashes.generated.tsobjects-*-echo-decisions.test.tspackages/spec/src/ui/action.zod.ts:1035-1036content/docs/protocol/objectui/actions.mdx:97-100docs/qa/platform-checklist/areas/identity-auth.json(7 lines)packages/spec/src/ui/inline-action.test.ts:307 (the sign-in URL as a parse-acceptance input)plugin-authroute-ledger rows forPOST /api/v1/auth/link-social,packages/clientauth.accounts.linkSocialH2, translations. The regenerated diff is 90 removed lines and 1 added: 4 times 17 lines of the
link_socialblock, 3 times 7 provenance rows, and the oneenunlink question rewritten from source. No other key moved. The echo ledgers drop exactly the seven provider rows each, andpnpm check:i18nandpnpm check:i18n-stale-fillare green (below).H3, declaration reach, measured on built
dist/**/*.d.ts:link_socialappears in 2 declaration files,dist/identity/index.d.tsand.d.mts, once each. It sits inside the type argument ofSysAccount's declared type, which isObjectSchema.create's return type:OmitofServiceObjectwithoutfields, intersected with aPickof the literal that keeps onlyfields. So no reachable member carries it. Atscprobe on that build compiledconst probe: ActionName = 'zzz_not_an_action', whereActionNameis the type ofSysAccount.actions[number].name, which therefore resolves tostring. The control line in the same file, a non-field assigned tokeyof (typeof SysAccount)['fields'], failed with TS2322 as expected.NonNullableofTranslationData['objects'], so no key types reach./appsor./metadata-translations.So the exported types are structurally unchanged and the narrowing is runtime and wire only. The
Clause-②: no (narrowing)arm stands as declared.H4, consumers. No workspace code, test, example app or dogfood test reads the action by name outside the H1 rows. objectui at the pinned
.objectui-sha0abd4f9f87, from a depth-1 fetch of that commit:git grep -e link_social -e 'Link Social' -e linkSocial -e link-socialgives 0 hits (exit 1). The controlgit grep sys_accounton the same tree gives 4 hits (CHANGELOGs), so the grep reached the tree. objectui's own ActionRunner tests use the sign-in URL as a generic url-action fixture and import nothing from here. No objectui change and no pin bump are needed (Post-Task Checklist item 4).H5, stored data. None. The action is code-shipped metadata registered at boot, never a row.
sys_accountisprotection.lock: 'full', soevaluateLockForWriterefuses every overlay save withITEM_LOCKED, and nosys_metadataoverlay can carry the action. No migration, seed, example or dogfood fixture names it.Tests
pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 src/identity/sys-account-link-social-retired.test.tsreadsTests 11 passed (11).pnpm --filter @objectstack/platform-objects testreadsTest Files 60 passed (60) · Tests 960 passed (960).pnpm --filter @objectstack/platform-objects typecheckexits 0;check:test-typecheckputs the new pin and the edited ledgers in its program (--listFilesOnly).pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/action-execution-destructive.test.tsreadsTests 66 passed (66); it reads the identity actions off their real declarations.pnpm --filter @objectstack/spec check:generated: all 15 generated artifacts up to date against the rebuilt specdist.Reverse verification (on committed HEAD
a299763014, throughscripts/ablation-replace.mjs, which restores on exit, INT and TERM):55baacb20fto1faf2af6f8.pnpm --filter @objectstack/platform-objects buildran, andablation-dist-preflightfound the marker in 6 built files. The pin went red:Tests 2 failed, 9 passed (11), on "declares exactly one action" and "no action targets a social sign-in or link door". Restored: the blob equals HEAD andgit diff HEADis empty. Rebuilt, preflight--absentread the marker absent from all 66 built files with a clean tree, and the pin is green again.link_socialleaf put back in theenbundle. The anchor hit once and the blob moved0d214ad760to3bddbb31f8. The pin went red:Tests 1 failed, 10 passed (11), on "en: sys_account._actions holds unlink_account and no link_social". Restored the same way: blob equality and an emptygit diff HEAD. No build was needed for this leg, because the pin imports the bundle by relative source path, not throughexports.Whole-repo pin sweep. Pins asserting
sys_account's action set, count orlink_socialkeys were swept repo-wide. They are the three echo ledgers (re-pinned on their new row counts, which assert the substance: 38, 39 and 50 rows) plusplatform-objects.test.ts,action-confirm-one-dialog.test.ts,confirm-question-carryover.test.ts,action-predicate-sparse-face.test.tsandruntime'saction-execution-destructive.test.ts, which readunlink_accountor iterate the object's actions and need no change. The new pin asserts the action set itself (['unlink_account']), not only that a name is gone.Gates
Taken at HEAD
c2bea2c789, after the last merge oforigin/main.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, derived 109 commands, and all 109 were run.--ranreconciles them:✓ dispatch-gates --ran: 109 derived famil(ies) accounted for — 109 run, 0 NOT-MEASURED.@objectstack/specgates refused withPREREQUISITE NOT MET(exit 3), because the merge had moved spec test files and the specdistinput digest no longer matched. Afterpnpm --filter @objectstack/spec build, the whole spec family (22 commands) was rerun, and all of it is green.check-closing-target-claimandcheck-single-claim-pathsanswer NOT WIRED without a PR number (exit 2) and are rerun once this PR exists.check-partof-closing-keywordwas run against this body throughPR_BODYand passes.check:adr-symbol-anchors,check:scripts-symbol-anchors,check:spec-docblock-symbol-anchorsandcheck:adr-anchors, exit 0.check:i18n,check:i18n-stale-fill,check:nul-bytes,check:adr-0087-registration,check:changeset-no-major,check:empty-changeset,check:yaml-examples,check:docs,check:api-surface,check:keyed-text-boundsandcheck:platform-object-tenancy-census.cc34db92de, before the checklist revert, was green apart from the same three PR-context gates.check:pm-dispatch-gatesis not derived for this diff.Acceptance notes
.changeset/21846-implicit-account-linking-ownership.md) tells a refused user to "link the provider from account settings". After this PR no console surface links. It is noted for that PR's holder, not filed.docs/NORTH-STAR.md(governed) nameslinked-accounts-socialon its identity line. The item id does not change, so no governed edit follows from this PR.packages/spec/src/ui/inline-action.test.ts:307keeps the sign-in URL as a parse-acceptance input. It asserts parsing only, not that the target works.Generated by Claude Code