Repository navigation
ADR (mirror of cloud ADR-0024): Identity & Access Architecture — the open half that plugin-auth / platform-objects/identity implement, recorded in this repo with Provenance #14506
Description
Activity
- added a parent issue
on Sep 2, 2026 - addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Sep 2, 2026 分诊路由(本评论来自分诊座位)· R+150 —
domain:services·priority:p2(保留documentation+pm:queue)。判据实测于本卡自陈的实现锚:
packages/plugins/plugin-auth/src/**与packages/platform-objects/src/identity/{sys-sso-provider,sys-user}.object.ts(git ls-files实测三个 identity object 文件都在packages/platform-objects/src/identity/下)。交付物是docs/adr/**的散文、不落在任何包里 ⇒ 锚定规则的「落点包」判据不适用,改用「谁能把它写对」:D1/D4/D5.2/D6 的机制半边由plugin-auth执行 ⇒ services 车道。三份镜像 ADR 同归一车道的完整理由、以及 p2 不提级为 p1 的判据,写在父单 #14496 的分诊评论
5545404025,⛔ 不在此重复。受管面 ⇒ draft PR + 人工合并(卡面已写明)。
Generated by Claude Code
Claim:
domain:servicesPM seat · sessionsession_01XpTx2tbq3pZRYAdoGt6E6Y· 2026-09-07分支
claude/issue-14506-adr-mirror-identity-access· worktreeobjectstack-issue-14506
Clause-②: no(记录,不是契约变更 —— 与卡面自陈一致;diff 不会碰packages/spec/**)⛔ 受管面:交付物在
docs/adr/**。draft PR + 人工合并,⛔ 席位不 flip ready、⛔ PM 不 arm auto-merge。派发前 PM 实测(⛔ 不是按卡面复述)
- 父卡 [Decision] Where does a decision that governs OPEN code live? — cloud (private) ADRs are cited from this public repo in 60 files with a qualifier and ~110 more times bare, squatting on unrelated local numbers (0024 · 0071 · 0081); mirror them here, or qualify only #14496 已裁:维护者 2026-09-02 逐字「ok」,裁方案 2(镜像开源半边,不搬文件),见评论
5507556468。该裁定同笔写明 [Decision] Where does a decision that governs OPEN code live? — cloud (private) ADRs are cited from this public repo in 60 files with a qualifier and ~110 more times bare, squatting on unrelated local numbers (0024 · 0071 · 0081); mirror them here, or qualify only #14496 本身是协调节点、⛔ 永不派发;四张 sub-issue 因此成为候选。本卡是其中之一 ⇒ 已裁可派。 - 我此前记录的阻塞「无 cloud 仓读权限」,对本卡是假的。 卡面正文已把 cloud ADR-0024 的开源半边逐条列全(D1 · D2 · D4 · D5/D5.2 · D6 · D7 · D9),并逐条列明留在 cloud 的部分(D3 · D5.1 · D8 · D10 · 路线图与商业框架),还点名了本仓的实现锚点。⇒ 镜像可以只凭本卡 + 本仓代码写出来,这正是总监席把内容写进卡面的原因。⛔ 不需要打开 cloud。
- ADR 编号已由 PM 分配,⛔ 不要自己取「下一个空号」:
origin/main(5a9138703) 上最高是 0131;且 0133 已被在飞的 PR docs(adr): ADR-0133 — organization management, the open basics; mirroring cloud ADR-0081 D1/D3/D4 with Provenance #16267(卡 ADR (mirror of cloud ADR-0081): Organization management — the open basics half (D1 invitations stay open, D3 org record page, D4 org-scopedsys_member), recorded in this repo with Provenance #14508)占用(实测git diff --name-only origin/main...pull/16267/head→docs/adr/0133-org-management-open-basics.md)。- 本卡用
0132。 - 姊妹卡 ADR (mirror of cloud ADR-0071): env-side SCIM via
@better-auth/scim— the open mechanism half (D1–D4), recorded in this repo with Provenance and the measured 1.7.x vendor drift #14507 用0134(同批并行派发)。 - 若你开分支时
0132已被占,停下来报回,⛔ 不要自行改号 —— 并行席位会撞。
- 本卡用
Refs: #14496(裁定)· #14361(改指,阻塞于本卡)· ADR-0079(Provenance 体例)
Generated by Claude Code
- 父卡 [Decision] Where does a decision that governs OPEN code live? — cloud (private) ADRs are cited from this public repo in 60 files with a qualifier and ~110 more times bare, squatting on unrelated local numbers (0024 · 0071 · 0081); mirror them here, or qualify only #14496 已裁:维护者 2026-09-02 逐字「ok」,裁方案 2(镜像开源半边,不搬文件),见评论
os-dev-report
{ "issue": 14506, "status": "ok", "status_in_base_vocabulary": "done", "branch": "claude/issue-14506-adr-mirror-identity-access", "pr": "https://github.com/objectstack-ai/objectstack/pull/16478", "pr_number": 16478, "pr_is_draft": true, "head_sha": "e1e5d0baa46b3b8d8b122ef5f44e201b94449b31", "base_at_branch_cut": "5a9138703d7461d2f586486509b2035d15450c16", "adr_file": "docs/adr/0132-identity-and-access-architecture.md", "adr_number_collision_check": "none — 0132 was free on origin/main 5a9138703 (highest 0131); pnpm check:adr-anchors green (it enforces one decision per number)", "premise_still_valid": true, "summary": "Wrote docs/adr/0132-identity-and-access-architecture.md (272 lines, the only changed file): this repo's own record of the OPEN half of cloud ADR-0024 — D1 per-env identity, D2 two populations / two sources of truth, D4 managed-vs-env-native marking, D5 identity-from-IdP with authorization decided in the env, D5.2 the SSO-era local user-management surface, D6 per-env SSO incl. the opt-in domain verification cited as ADR-0024 ②, D7 portability/self-host, D9 env users + better-auth organization membership. Slug reuses the cloud original's slug, per ADR-0079's stated precedent. Provenance section first, per the card. The commercial half (D3, D5.1, D8, D10, V1) is carried as an explicit table of what STAYS in cloud, plus a 'What this record does NOT settle' section — no decision added, none widened. Every clause carries a symbol anchor (path#symbol, never a line number) that I opened and read; all resolve under check-adr-symbol-anchors. PRIORITIZATION.md deliberately NOT edited — see notes_on_card_and_brief. Draft PR opened; skip-changeset applied and read back. ⛔ Not flipped ready, ⛔ no auto-merge, ⛔ no update_pull_request_branch.", "measured_adr_0024_bare_citations": { "command": "git grep -n -E 'ADR-0024' -- packages/plugins/plugin-auth/src packages/platform-objects/src/identity | grep -v -i 'cloud ADR-0024' | wc -l", "at_5a9138703_this_branch_base": 64, "at_00ff228fe_the_commit_the_card_cites": 59, "unfiltered_at_5a9138703": 76, "unfiltered_at_00ff228fe": 71, "verdict": "The card's '~71' does NOT reproduce with the card's own command. 71 is the UNFILTERED count at 00ff228fe; the filtered command there yields 59, and on today's origin/main 64. Reported as measured, not smoothed. Matters to #14361, whose scope figure comes from the same reading.", "checkout_was_shallow": false }, "cloud_recheck": "SKIPPED — NOT RUN. The first re-check on the card needs the cloud repo (git -C ../cloud show origin/main:docs/adr/0024-...). This seat has no cloud checkout and no cloud access. Nothing in the ADR is derived from the cloud file; the record's own Provenance section declares that limitation in the document, so a later reader is not misled about what was checked.", "governed_merge_readings": { "positive": { "command": "node scripts/pm/check-governed-merges.mjs --test docs/adr/0132-identity-and-access-architecture.md", "exit": 3, "verdict_line": "GOVERNED — a human merge is the review record for this PR (#9495 regime). docs/adr/** x1" }, "negative_control": { "command": "node scripts/pm/check-governed-merges.mjs --test packages/plugins/plugin-auth/src/auth-manager.ts", "exit": 0, "verdict_line": "NOT governed — ordinary queue landing applies to a PR with exactly this file list." } }, "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands — 17 commands; re-derived after the final commit and byte-identical to the pre-commit derivation", "union_run_at_commit": "e1e5d0baa", "exit_code_capture": "each command redirected to its own file, EXIT captured on the next statement; ⛔ never through a pipe", "results": [ { "cmd": "node scripts/check-adr-links.mjs", "exit": 0 }, { "cmd": "node scripts/check-adr-links.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-adr-symbol-anchors.mjs", "exit": 0 }, { "cmd": "node scripts/check-adr-symbol-anchors.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-ci-filter-parity.mjs", "exit": 0 }, { "cmd": "node scripts/check-closing-keyword-parity.mjs", "exit": 0 }, { "cmd": "node scripts/check-closing-keyword-parity.mjs --self-test", "exit": 0 }, { "cmd": "node scripts/check-comment-mask-corpus.mjs", "exit": 0 }, { "cmd": "pnpm --filter @objectstack/lint run check:doc-formula-expressions", "exit": 0, "note": "FIRST RUN WAS EXIT 3 = PREREQUISITE NOT MET (@objectstack/formula not built) and measured NOTHING — ⛔ not read as green. Cleared by turbo build --filter=@objectstack/formula --filter=@objectstack/lint under the shared verify lock, then re-run to this 0." }, { "cmd": "pnpm check:adr-anchors", "exit": 0 }, { "cmd": "pnpm check:cross-package-test-inputs", "exit": 0 }, { "cmd": "pnpm check:doc-authoring", "exit": 0 }, { "cmd": "pnpm check:driver-memory-census", "exit": 0 }, { "cmd": "pnpm check:nul-bytes", "exit": 0 }, { "cmd": "pnpm check:pm-governed-merges", "exit": 0 }, { "cmd": "pnpm check:refd-timer-probe", "exit": 0 }, { "cmd": "pnpm check:watch-hint-literal", "exit": 0 } ], "not_measured_declared": [ "check-cross-package-test-inputs --union-into RUNNER_TEMP/turbo-ls.json --changed RUNNER_TEMP/changed-files.txt — takes a value from the workflow, no value outside a CI run", "check-shard-attestation --emit --job test --shard MATRIX --total 6 — same", "check-test-completeness RUNNER_TEMP/test-core.log ... — same", "10 families whose declared population is too wide to place, and 41 artifact-roster families scored silent for every card in the tree — ⛔ neither group is a clearance; none of their rosters sits in a directory this PR's single path is in" ] }, "changeset": "none; skip-changeset label applied and read back. Precedent measured, not recalled: PRs 14976 (ADR-0131), 14151 (ADR-0130), 12519 (ADR-0127) are ADR-only landings with NO .changeset file and the skip-changeset label; 13067 (ADR-0129) carried a changeset because it also changed spec + plugin-auth code. Label write route: container REST probe returned 403 for repo-scoped reads, so the additive REST POST endpoint was unavailable this session; fell back to MCP read (documentation, size/m) → union → whole-set write → comparative read-back, which returned documentation + size/m + skip-changeset with draft still true. Channel switch declared here.", "tests": "No code changed, so no package test/typecheck applies; the deliverable's verification IS the gate union above, run at e1e5d0baa and listed command-by-command with the exit code captured after its own redirect. Additionally: every code anchor written into the ADR was opened and read before being written (plugin-auth auth-plugin.ts / auth-manager.ts / objectql-adapter.ts / last-admin-guard.ts / invitation-role-cap.ts / member-role-canonical.ts / register-sso-provider.ts / auth-schema-config.ts / set-initial-password.ts, platform-objects identity sys-user / sys-member / sys-account / sys-sso-provider objects, core resolve-authz-context.ts), and check-adr-symbol-anchors independently proves each anchor resolves to a real declaration site with comments stripped. Control-character self-scan beyond the gate: grep -naP over the new file, zero hits.", "mcp_calls": "10 — search_issues x1 (dedup for the finding), issue_write create x1 (#16477), create_pull_request x1, issue_read get_labels x2 (BOTH FAILED: 'Could not resolve to an Issue with the number of 16478' — that method does not resolve a PR number in this session), pull_request_read get x1, list_pull_requests x2 (label read + read-back), issue_write update x1 (labels), add_issue_comment x1 (this comment). REST was 403 on a repo-scoped probe at the start of the run, and gh is absent, so issue/PR body reads went through the zero-quota public-repo page payload instead.", "open_questions": [], "out_of_scope_findings": [ "filed as #16477 (label `finding`, unassigned): five plugin-auth comments still point at `last-admin-ban-guard.ts`, a filename that has not existed since the guard was renamed to `last-admin-guard.ts` — invitation-role-cap.ts, objectql-adapter.ts, break-glass-local-credential.test.ts x2, and objectql-adapter.test.ts naming a `last-admin-ban-guard.test.ts` as where the 403 arm is pinned (that file does not exist either). ⛔ Not fixed in this PR: docs/adr/** only." ], "notes_on_card_and_brief": [ "CARD RE-CHECK FIGURE IS WRONG AS WRITTEN. The card annotates its second re-check `# expect ~71` on a command that includes `grep -v -i 'cloud ADR-0024'`. At the card's own cited commit 00ff228fe that command yields 59; 71 is what the command yields WITHOUT the filter line. On today's origin/main the filtered figure is 64. Reported unsmoothed; #14361 should re-measure rather than inherit 71.", "CARD'S 'next free number / 133 records today; 0130 is the highest' IS STALE and the PM brief correctly overrode it. On origin/main 5a9138703 the highest is 0131, and 0133/0134 are spoken for. Used the PM-assigned 0132 and did not pick a number myself.", "PRIORITIZATION.md IS NOT A HAND-MAINTAINED INDEX, so the card's conditional ('register the file there IF that index is maintained by hand — read the file's own header') resolves to NO. Its header, verbatim: '⚠️ STALE (noted 2026-07-16): this review predates ADR-0050 onward ... Treat it as a historical snapshot, not current state. The Status: header of each ADR file (kept current per the hygiene rule below) is the authority; this document is retained for its method and its ranked-plan rationale.' It is a dated 2026-06-12 review of 'All 49 ADRs in docs/adr/'; no ADR from 0050 up is listed. Adding a 2026-09 record to it would falsify a snapshot. docs/adr/ holds no other index file. ⇒ Not edited.", "SLUG CHOICE, stated because it is a judgement the card left open: the file is `0132-identity-and-access-architecture.md`, reusing the cloud original's slug. ADR-0079 — the shape the card told me to copy — states that precedent in its own Provenance section ('deliberately reuses the original's slug ... so the two are recognisably one record'). Rename is cheap if the reviewer prefers a `-open-half` suffix.", "A DETAIL WORTH #14361'S ATTENTION, recorded in the new ADR rather than assumed: a bare `ADR-0024` in this tree cannot be re-pointed mechanically, because some sites mean a clause that STAYS in cloud — `packages/plugins/plugin-auth/src/auth-manager.ts` cites `ADR-0024 V1` for the SSO default-role provisioning, and V1 is on the card's own stays-in-cloud list. The same trap sits at 0071: cloud ADR-0071 is env-side SCIM, while this registry's 0071 is the dataset semantic-layer depth record. Both are written into the record.", "PR BODY COSMETIC, disclosed rather than silently left: one blockquote line in the PR body renders as a literal '>' instead of a quote marker (I HTML-escaped it when passing the body). Text is intact and readable; ⛔ not edited, because editing a PR body downgrades the create-only session-URL footer and risks the draft bit, which on a governed surface is the more expensive mistake." ] }
Generated by Claude Code
⛔ PM 更正:我分配的 ADR 号
0132撞车。改为0135我在认领评论(
5564491920)里把本卡的编号定为0132,并写着「origin/main(5a9138703) 上最高是 0131;且 0133 已被在飞的 PR #16267 占用」。那次测量不完整 —— 我只查了main和 #16267,没有扫全部开放 PR。实测:PR #16215(
claude/issue-16130-open-org-scoping,标题即写着(ADR-0132))在它的分支上加了docs/adr/0132-multi-organization-runtime-is-open-core.mdgit diff --name-only origin/main...pull/16215/head命中。它在main上确实是空号,但已被一个开放 PR 认领。⇒ 席位按我的指示写出的 PR #16478(docs/adr/0132-identity-and-access-architecture.md)与它撞。⚠️ 这件事本来是可查的:PR #16267 的正文里就写着 —— "#16215's branch addsdocs/adr/0132-…, so 0132 is claimed by an open PR even though it is free onmain",而且那位席位当时逐一扫了全部 40 个开放 PR。我派发前没读到它。当前占用(实测)
号 占用者 ≤0131 已落在 origin/main0132 PR #16215(开放) 0133 PR #16267(卡 #14508,第三份镜像,开放) 0134 PR #16476(卡 #14507,姊妹卡,开放) 0135 空 ⇐ 本卡 已把改号指示发回席位:重命名文件(slug 不变)、更新文件内 1 处自引、改 PR 标题与正文并写明改号原因、重跑
check-adr-anchors/check-adr-symbol-anchors/check:pm-governed-merges。普通提交即可,⛔ 不需要 force-push。⭐ 席位本身没做错,两条已由 PM 实测确认
- 0 处
path:NNN行锚点。 这条要紧:check:adr-symbol-anchors自 2026-09-01 的裁定起把docs/adr/**下任何存活的行锚点判为硬失败、无过渡期(背后普查测得 337 个行锚点中 243 个已失效)。⚠️ 而本卡正文供的三个锚点是file.ts:NN形式 —— 席位没有照抄,是对的;我的派发简报也没提醒这条,应当提醒。 - 没有引用姊妹镜像尚未落地的号(0133 / 0134)。引用
docs/adr/下不存在的号会被check-adr-anchors判为 squat 而失败。
⛔ 受管面不变:PR 保持 draft、人工合并、不 arm auto-merge。
Generated by Claude Code
- 0 处
os-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:queue.- Closing pull request: docs(adr): ADR-0135 mirrors the open half of cloud ADR-0024 — identity & access architecture #16478, merged.
- Closing commit
3335bd35ea, merged intomain. - Left untouched:
documentation,priority:p2,domain:services— ownership, priority and outcome are not state claims. - The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.posted by half-state-patrol run 34097314575 · trigger
scheduleGenerated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Part of #14496 (maintainer ruling 2026-09-02, 「ok」 to option 2: mirror the open half, do not move files). Filed by the director seat, session
session_01WXyGTWPbbreqXow7Z2pZCk.docs/adr/**is a governed surface: draft PR, human merge, no seat flips it ready. Clause-②: no (a record, not a contract change).domain:*is triage's.What to write
A new local ADR under the next free number in
docs/adr/(133 records today; 0130 is the highest) that states, as this repo enforces them, the decisions of clouddocs/adr/0024-identity-and-access-architecture.md(Status: Accepted, founder, 2026-06-25) whose mechanism lives in open code. From the cloud record's own headings, the open half is:plugin-authruns better-auth in the env);sys_user/sys_accountmanagedByposture);sys_useractions, break-glass password, the last-admin guard that cites "ADR-0024 D4/D5.2" today);@better-auth/ssomechanism andsys_sso_providerincl. the opt-in domain verification cited as "ADR-0024 ②");Stays in cloud, cited as
cloud ADR-0024: D3 cloud-as-IdP hub, D5.1 the cloud OP membership gate, D8 billing, D10 prod/dev metering and population lifecycle, the V1 roadmap and commercial framing.Shape (copy
docs/adr/0079-record-display-name.md)## Provenance — read this before citing this filesection first: this file records, in this repo, decisions taken inobjectstack-ai/cloudADR-0024 on 2026-06-25; the cloud record is the original and keeps the commercial half; this file is written from the cloud record and the code that implements it, with the implementing anchors named (packages/plugins/plugin-auth/src/**,packages/platform-objects/src/identity/sys-sso-provider.object.ts,sys-user.object.ts,last-admin-guard.ts).docs/adr/PRIORITIZATION.mdif that index is maintained by hand (check the file's own header);scripts/adr-anchors/mapping for the files that will cite it is the re-pointing card's job ([finding] The SCIM/identity ADR-0071 citation resolves to the dataset semantic-layer record — 39 files point at a decision about multi-hop joins #14361), not this one's.Follow-ups the accepting seat files (not this card)
repo:cloudseat card; number known only after this lands).ADR-0024citations (71 measured onorigin/main@00ff228fe) to the new local number; ⛔ not done here.Re-check
Refs: #14496 (the ruling) · #14361 (the re-pointing, blocked on this) · cloud ADR-0024 · ADR-0079 (the Provenance shape) · #14360 / #13816 (live identity work that cites these decisions).