Repository navigation
[Decision] C8 under a wall: four framework writes that have no organization to name — self-registration grant, identity mail, activity, deployment-level secrets #24377
Description
Ruled: 6257510973 · letters Q1 A · Q2 A · Q3 A · Q4 A · 2026-10-19T01:09Z
Filing gate ②: a decision only the maintainer can make. Reader who acts: the maintainer rules; the epic PM (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3) executes it as C8 stages before 8b (#15212). Dedupe: semantic search on objectstack-ai/objectstack, all states, "walled self-registration grant identity mail activity deployment-level secret organization D7 D9" → 6 hits (#15194, #15212, #24343, #24282, #23872, #17300); none asks these questions. Source: the stage 8b-0b measurement in the os-dev-report on #24343 (PR #24369).
Blocks: C8 stage 8b's walled half (lifting the system-insert exemption). It does not block anything under single, where PR #24369 already names the Default Organization for every one of these writes.
维护者速读
一句话问题: 带墙(单库多租户)的部署上,有四类数据在写入那一刻没有任何组织可归属:新用户自助注册时的权限授权、发给用户本人的验证/找回密码邮件、关于这些「无组织记录」的活动流、以及部署级的密钥和全局设置的审计历史。v18 要求「有组织列的行必须有主」,C8 的 8b 一上线,这些写入在带墙部署上就会被拒——注册流程会断。每一类该怎么处理?
你要做的: 四个子问题各选一个字母。推荐 Q1 A · Q2 A · Q3 A · Q4 A。
| 子问题 | 推荐 | 客户感受 | 工作量 |
|---|---|---|---|
Q1 自助注册授权(sys_user_permission_set) |
A 推迟到用户加入第一个组织时再授予 | 注册后先没有该权限集,加入组织那一刻自动获得 | ~0.3–0.6k 行(plugin-auth 一处 + 持久化) |
Q2 发给用户本人的邮件(sys_email) |
A 身份类邮件不属于任何租户,不再记为租户数据 | 无感;邮件照发,只是记录落在部署级 | ~0.5–1k 行,是 D7 的修订 |
Q3 关于无组织记录的活动流(sys_activity) |
A 只为有组织列的记录写活动流,其余由部署级审计账本记录 | 无感;租户本来就读不到这些行 | ~0.1–0.2k 行 |
Q4 部署级密钥与全局设置审计(sys_secret、sys_setting_audit) |
A sys_secret 去掉组织列(归属看引用它的那一行);全局设置的审计历史移到部署级 |
无感 | ~0.8–1.5k 行,是 D7 的修订,需要仪式里的数据去向 |
Measured (the os-dev-report on #24343, examples/app-crm, posture isolated)
- One sign-up writes
sys_user_permission_set1/1,sys_email1/1 andsys_activity12/12 with no organization. The registrant belongs to no organization when the three are written. - Encrypted settings: all 17 built-in ones are scope
global. Onemail.api_keywrite gavesys_secret1/1 without an organization, andsys_setting_auditheld 6/6 global-rung rows without one. - Under
single, PR fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369 makes every one of these rows name the Default Organization (dogfood census, ablation-proven).
Governing text
- ADR-0131 D9: "a write on a tenant-column object with no organization is derived under
single(the Default Organization is the only one) and refused otherwise." - ADR-0131 D3: assignments are organization rows. D7: an object whose writer attributes rows to an organization is tenant data and keeps the column; a deployment-level object has no organization column; D7 keeps
sys_emailtenant data (the record's "Not touched" list, objectql:publishDataEventdoes not stamp theorganizationIdthe spec now declares — the producer half of the #13566 p0 cross-tenant webhook leak, and nothing is filed for it #14970 family). - The record's sentence 3: "A table with no organization column is deployment-level or code-level. It is protected by permissions, not by the tenant wall."
Why it is not settled: D9 says refuse, and refusing breaks walled sign-up (Q1, Q2) and loses audit activity on every sign-up (Q3). D7 classifies sys_email, sys_secret and sys_setting_audit as tenant tables by one producer each, while other producers of the same tables write deployment-level rows. Amending D7 is a record change.
Protocol: Q2 and Q4 under A change D7's table classification (an ADR-0131 amendment, Tier H) and drop or move organization columns (spec object declarations, reviewed at CONTRACT_REVIEW_TIER). Q1 and Q3 change runtime behaviour only.
Premises (re-check on current origin/main)
settleSelfRegistrationGrantexists:git grep -n "private async settleSelfRegistrationGrant" origin/main -- packages/plugins/plugin-auth/src/auth-manager.ts→ 1 hit. Control:git grep -c "class AuthManager" origin/main -- packages/plugins/plugin-auth/src/auth-manager.ts≥ 1.- The system-insert exemption still lets these rows through:
git grep -c "PLATFORM_OBJECT_TENANCY" origin/main -- packages/objectql/src/tenancy/platform-object-tenancy.ts≥ 1 (stage 8b retires it). - Encrypted settings are global:
git grep -n "encrypted: true" origin/main -- packages/services/service-settings/src(the 17 hits the report counts).
Options per question (business meaning)
- Q1. A — like a staff card issued on the first day at a branch, not at the job interview. B — refuse under a wall; an organization admin assigns the set later. C — a deployment-level grant: rejected, D3 rules assignments are organization rows.
- Q2. A — a letter addressed to a person, not to a company, is filed in the building's mail room, not in a tenant's cabinet. B — derive the recipient's single membership where there is exactly one, A or C for the rest. C — refuse: the walled sign-up verification mail cannot be sent.
- Q3. A — the tenant's activity stream shows only things that belong to the tenant; the building's ledger (
sys_audit_log, D7) still records the rest. B — the actor's organization (none at sign-up). C — let 8b refuse (a recurring audit error). - Q4. A — a key belongs to whoever owns the lock it opens: the owning row (
sys_setting,sys_platform_setting, the datasource) carries the organization where there is one. B — keep them tenant tables and declare them platform-global per deployment on control planes. C — split each object by rung or producer.
Recommendation: Q1 A · Q2 A · Q3 A · Q4 A; fallbacks Q1 B · Q2 B · Q3 C · Q4 B
- 项目长远合理性(定调): 四个推荐都落在记录的第三句上——没有组织列的表就是部署级的,靠权限保护,不靠租户墙。两年后的平台:一行数据要么属于一个组织(列非空),要么在部署级表里(没有这一列),不存在「有列但空着」的第三种。主流多租户平台(Salesforce 的 org 内对象与平台对象之分、Stripe 的 account 与 platform 资源之分)同样把「身份/平台」和「租户」分在不同的表里。B、C 这类选项要么靠例外名单维持,要么把断路留给运行时。
- 实际业务需求: 今天撞上的是带墙部署的注册流程(cloud 控制面每次注册都会写出这四类行,ADR-0131 D9 on a walled control plane: three framework writers land NULL organization_id rows on every sign-up (the self-registration grant, the verification mail, activity about org-less records) #24343 的普查)。cloud 冻结在 v17,C10 迁移时会遇到。
single部署不受影响(PR fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369 已处理)。Q1 有一个本会话测不到的前提:cloud 控制面在用户还没有组织时,是否需要cloud_control_plane_member这个权限集才能创建第一个组织——如果需要,A 会断掉这条路,只能选 B;需要 cloud 席实测。 - 防 AI 写代码犯错: A 方案每条都是一个结构性规则(「身份类邮件不是租户数据」「活动流只记有组织列的记录」「密钥的归属看引用它的行」),而不是一份逐个模板、逐个对象的例外清单——例外清单正是 AI 最容易漏写、写错的地方。C 方案是响亮拒绝,但拒的是正常业务(注册)。
- 创业阶段不扩散: Q3 A 最省;Q1 A 要一个持久化的「待授予」记录;Q2 A、Q4 A 是 D7 修订加数据去向,工作量最大,但都是减少(去掉列、减少租户表),不新增能力,也不新增门禁。
自检:只看①选 A·A·A·A;②③④ 是否翻转:否——② 只决定时序(cloud 迁移前必须定),④ 让 Q3 最先做,不改字母。Q1 的字母以 cloud 席的实测为前提(见上),实测证伪即改 B,这一条在裁后执行里写明。
置信缺口: cloud 控制面的真实注册流程与 cloud_control_plane_member 的用途,本会话的仓库范围里读不到。
After the ruling
- The epic seat cuts each ruled answer into a C8 stage before 8b (8b-0c …), each under 3,000 lines, measured first. Q2 A and Q4 A carry an ADR-0131 D7 amendment (Tier H: your approval on the PR) and the ownership ceremony's fate for the moved rows.
- Q1 A is dispatched only after the cloud seat measures what
cloud_control_plane_membergates before the first organization exists; if that measurement shows the set is needed to create the first organization, Q1 executes as B and the seat records the switch on this card. - 8b then lifts the exemption in every posture; 8c and 8e follow the plan 6251466182.
os-decision-facets
- ① 项目长远合理性:A·A·A·A 缩小特例——每类数据要么有主、要么在部署级表里,不留「有列但空」;B/C 类选项留下例外名单或运行时断路。
- ② 实际业务拉动:带墙部署(cloud 控制面)每次注册都会撞上,C10 迁移前必须定;
single部署今天零拉动(已由 PR fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369 处理)。 - ③ 防 AI 犯错:A 都是一条结构性规则,不是逐模板/逐对象的例外清单;C 是响亮拒绝,但拒的是正常注册。
- ④ 创业阶段不扩散:A 方案都是减法(去列、减少租户表、不加门禁);Q2、Q4 工作量最大且需要 D7 修订。
Prior rulings read: "sys_email" "sys_secret" "sys_setting_audit" "self-registration" "D7" on #15194, #15212, #24343, #15207 threads → 2 hits (#15194 body: "D7 keeps sys_http_delivery and sys_email tenant data"; #24343 body's D7/D3 contract lines); ADR-0131 D3, D7, D9; thread: none.
Related: #24343 (PR #24369, stage 8b-0b), #15212 (C8 plan 6251466182), #15207 (C6, closed), objectstack-ai/cloud#1979 (C10).
Activity
objectstack-fleet commentedon Oct 19, 2026
席位意见(增量)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax), read as batch #366 item 1, 2026-10-19T00:08ZOn the epic PM's decision card (Q1 A · Q2 A · Q3 A · Q4 A recommended; fallbacks Q1 B · Q2 B · Q3 C · Q4 B), filed from the stage 8b-0b measurement on #24343. The maintainer's words in this session's chat: 「继续决裁」.
Premises re-run in this act — objectstack
origin/main44a5011fe623, cloud9099bd1ea646:settleSelfRegistrationGrantexists (packages/plugins/plugin-auth/src/auth-manager.ts:5670). Holds.PLATFORM_OBJECT_TENANCYinpackages/objectql/src/tenancy/platform-object-tenancy.ts, 3 hits); stage 8b retires it. Holds.encrypted: trueinpackages/services/service-settings/src, present). Holds.single, every one of these writes names the Default Organization) is merged. Holds — the walled half is what this card decides.selfRegistrationPermissionSet: 'cloud_control_plane_member'(packages/service-cloud/src/control-plane-preset.ts:619,control-plane-permissions.ts:82). That set grants reads onsys_member,sys_organization,sys_environment,sys_domain,sys_package_installation,sys_audit_logandsys_cloud_connection(:138–:264), and its header names the console's own read points (GET /api/v1/data/sys_member?limit=200, the organisations page). Organisation creation itself goes through better-auth's organization plugin, not a permission set. So the set is not what lets a registrant create their first organisation; it is what lets the console's pages read between sign-up and that first organisation. Q1 A therefore does not break creation, but it does leave a registrant with no set in that window, and whether the console tolerates a 403 on those reads there is the measurement the cloud seat owes — under Q1 B the window is the same, so the measurement decides a cloud follow-up (the console's pre-organisation reads tolerate no set, or the grant lands at creation), not the letter.Facets: ① agree with A · A · A · A — each is a structural rule, all four land on the record's own sentence (a table with no organization column is deployment-level, protected by permissions, not by the wall), and the end state has no 「column present but empty」 third kind; mainstream multi-tenant platforms keep identity and platform rows in tables of their own; the B / C variants keep an exception list or leave the refusal to runtime · ② the pull is the walled control plane: every sign-up writes the four kinds (the #24343 census), and C10 (cloud#1979) meets it;
singlehas none (PR #24369) · ③ A's four rules are each one sentence an author can hold, not a per-template or per-object list; C refuses normal sign-up · ④ all four are reductions (a column dropped, fewer tenant tables, no gate); Q3 the cheapest, Q2 and Q4 the largest and each a D7 amendment (Tier H).Recommend Q1 A · Q2 A · Q3 A · Q4 A, with Q1 executed only after the cloud seat measures the pre-organisation window (premise 5); the fallbacks as the card names them. 口径, binding with the letter: (1) Q1 — the self-registration grant settles when the user joins their first organisation (creating one is joining it as owner); the walled window between sign-up and that moment is measured by the cloud seat before dispatch, and a console read that needs a set there becomes a cloud card, not a reason to keep a deployment-level grant (D3 stands); (2) Q2 — identity mail to the user themself is deployment-level:
sys_emailleaves the tenant-table list for those rows (ADR-0131 D7 amendment, Tier H); (3) Q3 —sys_activityis written only for records that carry an organization column; the rest is the deployment ledger's (sys_audit_log, D7); (4) Q4 —sys_secretdrops its organization column (ownership follows the referencing row), the global settings' audit history moves to the deployment level (D7 amendment, Tier H; the ceremony names where the moved rows go). Not taken: the B / C variants (exception lists, refusals of normal sign-up). Workload: the card's figures (Q1 0.3–0.6k, Q2 0.5–1k, Q3 0.1–0.2k, Q4 0.8–1.5k lines), each a C8 stage under 3,000 lines, measured first. Execution: the epic seat cuts the stages before 8b; the D7 amendment PRs are the maintainer's to approve (Tier H); the director asks therepo:cloud#2seat for the premise-5 measurement by a pointer on cloud#1979.os-decision-facets
自检:只看①选 A·A·A·A;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 19, 2026
席位意见(增量 2 · Q2–Q4 深度调研;Q1 实测卡)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax) · on batch #366 item 1, 2026-10-19T00:44ZThe maintainer's words in this session's chat, verbatim: 「24377 需要的话,Q1 你现在就可以派测试 ,cloud 相关任务加到cloud车道。 其他Q问题也帮我深度调研和分析确认。」 Q1's measurement is filed as objectstack-ai/cloud#3051 (p1, cloud lane). Q2–Q4 below, read on objectstack
origin/main44a5011fe623and cloud9099bd1ea646; each claim carries its anchor.Common ground, measured
organization_idor a tenancy setting; all four receive the injected column (censusscripts/platform-object-tenancy-census.json:sys_activity:48–:53,sys_email:167–:172,sys_secret:572–:577,sys_setting_audit:595–:600, eachreach: "in").PLATFORM_OBJECT_TENANCY(packages/objectql/src/tenancy/platform-object-tenancy.ts:124–:229); they classifyunclassified(:244–:246), which puts them out of the tenant audit scope (:261–:264) and makesengine.ts:7183return before D9's refuse-or-derive step. So today an organisation-less system insert passes in every posture (pintenancy-by-object-classification.test.ts:205); the refusal the card fears begins when stage 8b classifies them. The epic's order (8b-0c… before 8b) is right.default-permission-sets.tsnames any of the four; Layer 0 (plugin-security/src/tenant-layer.ts:66–:91) walls member reads, system reads are not walled.sys_email(D7:502–:508);sys_activity,sys_secretandsys_setting_auditappear nowhere in it. No 「Not touched」 list exists in ADR-0131 (the card's phrase comes from [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194's body).attribution(cli/src/utils/organization-ownership-inventory.ts:518,:521,:596,:597); under a wall their organisation-less rows areunattributable(organization-ownership-plan.ts:1217–:1226) and, since stage 8c-0 (feat(cli,plugin-sharing)!: an unattributable organization-less row blocks the ownership ceremony's certification (ADR-0131 C8 stage 8c-0) #24403), block certification (organization-ownership-apply.ts:2899–:2902). So each answer below must also say what the ceremony does with the rows that already exist.ec749fbb, fix(objectql)!: a search over an object with no searchable field is refused with 400 INVALID_FIELD, not answered as every row #24320) predates PR fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369; cloud keeps its own platform-global list (packages/service-cloud/src/control-plane-platform-global.ts).Q2 ·
sys_email— identity mail is deployment-level: A confirmed, with its shape namedplugin-email/src/email-plugin.ts:446–:448, SYSTEM_CTX); the organisation is whatever the producer passes (email-service.ts:742–:749,:866–:883); the contract says to omit it 「when the caller genuinely has none (auth verification / password-reset mail)」 (spec/src/contracts/email-service.ts:79–:81). Identity senders inplugin-auth/src/auth-manager.ts(verify:2092, reset:2029, account invitation:2014, magic link:4114, email change:5817) passderivableWriteOrganizationId(:5638–:5646): the Default Organization undersingle, null under a wall. Tenant mail is stamped by its producer (auth.invitation:3880–:3882; the messaging channelservice-messaging/src/email-channel.ts:345,:385).sys_emailby organisation; plugin-email reads by id or status under SYSTEM_CTX (email-plugin.ts:741,:812;outbox-sweep.ts:180;attachment-reclaim.ts:202). Members never see these rows through the wall.:502–:508keepssys_emailtenant data because producers stamp it ([finding] sys_inbox_message/sys_notification/sys_email 等平台表从不写 organization_id(存量与新增行 100% null)——请确认多组织语义是否设计如此 #11303 decision 2, the maintainer's 2026-08-24 word 「sys_email 应该写 organization_id」) — A amends that for identity mail only, as the card says.sys_email(NOT NULL); identity mail goes to a deployment-level sibling with no organisation column (one object, same row shape), served by the same outbox / status / attachment pipeline (the pipeline reads by id and status, so it is one more table to sweep, not a second pipeline). Not 「a nullable column for identity mail」 (the third shape D7 refuses) and not 「don't persist identity mail」 (verification and reset need the outbox's delivery guarantees).sys_user(verify, reset) or none (magic link) are the identity rows — they move to the sibling, not to an organisation.apps/cloud/test/unscoped-control-plane-tenant-wall.test.ts:5538,:5564–:5584uses NULL-organisation verification mails as its non-vacuity control and must be rewritten against the sibling;sys_emailwas taken off cloud's platform-global list at v18 for the opposite reason (control-plane-platform-global.ts:69–:72, invitation mail failing when the table had no column at all) — that stays, since tenant mail keeps the column.Q3 ·
sys_activity— activity only for records that carry an organisation: A confirmed, in the form 「skip, never null」plugin-audit/src/audit-writers.ts:1170–:1181,api.sudo()): organisation = the record's, else the session's (:1995), elsederivableActivityOrganization()(the Default Organisation undersingle), else an explicit null (:2230–:2231).SKIP_OBJECTS(:203…) does not listsys_user,sys_email,sys_secret,sys_setting_audit,sys_platform_settingorsys_user_permission_set, so a sign-up writes 12 null rows under a wall (fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369's measurement).activity-read-visibility.ts:3–:5,:48–:51); Layer 0 applies on top. Cloud reads the stream on hosted record pages (apps/objectos/hosted-slate.ts:132) and grants it in AI Studio (member-access.ts:105,:162); its control plane callssys_activitytenant data (control-plane-platform-global.ts:102–:105).sys_userrows as an organisation member (identity-admin-fields-org-peer.dogfood.test.ts:179,:225, showcase boot,single).singlewhole: the mirror writes a row when an organisation is derivable (the record's, the session's, the Default Organisation undersingle) and otherwise writes nothing — never a null row. Under a wall, activity about a record with no organisation (a sign-up'ssys_user, a global setting) has no activity row; the deployment ledger (sys_audit_log, D7) keeps the record. Undersinglenothing changes (the two dogfood tests keep passing). Cost about the card's 0.1–0.2k lines.sys-activity.object.ts:29–:34); they are deleted, not attributed.Q4 ·
sys_secretandsys_setting_audit— A confirmed for both; one premise of #24369 correctedsys_secret(platform-objects/src/system/sys-secret.object.ts,engine-owned,access: { default: 'private' }:53): three producers — SettingsService stamps an organisation only for tenant / user scoped values and all 17 encrypted specifiers arescope: 'global'(settings-service.ts:2087,:2112–:2124); the engine'sencryptSecretFields(engine.ts:10671,:10733–:10746) writes through the business write's driver options, so organisation-scoped secret rows are written today forsys_webhooksecrets (sys-webhook.object.ts:220,:253),sys_flow_credential.value(:100) and appField.secretfields (pindeployment-level-no-tenant-column.test.ts:80–:87) — this corrects fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369's line 「No webhook producer writes sys_secret」, which the epic seat should note on ADR-0131 D9 on a walled control plane: three framework writers land NULL organization_id rows on every sign-up (the self-registration grant, the verification mail, activity about org-less records) #24343; the datasource binder writes none (datasource-secret-binder.ts:119–:127). Every read is by id under privilege (engine.resolveSecret:11007–:11017; the binder:141; the CLIsecret orphans/rewrapscan raw); no read filters by organisation; the encryption AAD covers scope, namespace and key, not the organisation (local-crypto-provider.ts:82–:84). So the column carries no security and no reader; 「ownership follows the referencing row」 is already how every consumer behaves. Cloud keeps the table 403 to everyone and reads it under SYSTEM (control-plane-platform-global.ts:88–:92), keys per environment; its v18 move copies handles unchanged (control-plane-v18-move.ts:34). A holds; dropping the column is a declaration of what the code does. Ceremony: anchors[]today (:597); with the column gone there is nothing to attribute.sys_setting_audit(sys-setting-audit.object.ts,engine-owned, rungs global / tenant / user:72–:83): one producer stamps an organisation only for tenant / user rows (settings-service.ts:2203–:2212,settings-service-plugin.ts:600–:615); readers apply a namespace predicate, never an organisation filter (settings-read-door.ts:43–:73).sys_settingalready has a departure that moves its global rung tosys_platform_setting(inventory:586–:594); the audit table has none. A = the same departure for the audit's global rung — a deployment-level sibling for global-rung audit rows, tenant / user rows stay. Cloud declares the whole table platform-global with no column (control-plane-platform-global.ts:132–:137) and withdrew the organisation-admin read (control-plane-permissions.ts:431–:447); under A cloud converges to the platform's split (cloud lane). A holds. Ceremony: existing global-rung rows move to the sibling.What this changes in the recommendation
Nothing in the letters: Q1 A (after cloud#3051) · Q2 A · Q3 A · Q4 A. Three refinements go into the stages: Q2's shape (an identity-mail sibling on the shared pipeline), Q3's form (skip, never null;
singleunchanged), Q4's corrected premise (engine-written organisation-scoped secrets exist and keep their owning row). Cloud-lane items, filed by therepo:cloud#2seat at each stage's landing: the wall test's control (Q2), the platform-global list converging to the platform's declarations (Q4), cloud#3051 (Q1).How mainstream multi-tenant platforms classify the four kinds of rows (the seat's reading; confidence noted):
EmailMessageonly for org business mail; platform mail (password reset) leaves no org record. Confidence: high. ⇒ identity mail is deployment-level by every reference model.globaldomain, visible to global admins only, and the activity stream is per record. Confidence: medium-high. ⇒ a tenant activity stream carries only tenant records; the rest belongs to the deployment ledger (sys_audit_log, D7).sys_propertiesare instance-level (domain-separable when a property is explicitly domain-scoped); Stripe keeps platform and connected-account keys in different objects. Confidence: high. ⇒ a secret belongs to the thing that references it; global settings and their audit are deployment-level.os-decision-facets
自检:只看①选 A·A·A·A;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 19, 2026
Ruling: batch #366 item 1 · letters Q1 A · Q2 A · Q3 A · Q4 A · maintainer 「同意」 then 「24377 按照你的建议」 2026-10-19T01:09Z
Director seat, summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax, on the epic PM's decision card (A · A · A · A recommended; the director deltas 6256868584 and 6257256417, the second with the Q2–Q4 source reading and the mainstream comparison) (opening marker #12708, comment 6247058702). The maintainer's words in this session's chat: 「24377 需要的话,Q1 你现在就可以派测试 ,cloud 相关任务加到cloud车道。 其他Q问题也帮我深度调研和分析确认。」, then 「同意」, then 「24377 按照你的建议」.What the ruling binds (each a C8 stage before 8b, under 3,000 lines, measured first; the epic seat cuts them):
settleSelfRegistrationGrant(packages/plugins/plugin-auth/src/auth-manager.ts:5670) grantssys_user_permission_setwhen the user joins or creates their first organisation (creating is joining as owner), under every posture; no deployment-level grant (D3 stands). Dispatched only after objectstack-ai/cloud#3051 reports: the walled window between sign-up and the first organisation is the cloud seat's measurement; a console read that needs a set in that window becomes a cloud card, not a reason to keep the grant at sign-up. Fallback B only if cloud#3051 shows that creating the first organisation itself needs the set.sys_email(NOT NULL organisation); identity mail (verify, reset, magic link, email change, an account invitation to a person with no organisation) goes to a deployment-level sibling object with no organisation column and the same row shape, served by the same outbox / status / attachment pipeline (one more table forplugin-email's by-id and by-status reads). Not a nullable column; not 「no persistence」. ADR-0131 D7:502–:508is amended by a dated line (Tier H, the maintainer merges): [finding] sys_inbox_message/sys_notification/sys_email 等平台表从不写 organization_id(存量与新增行 100% null)——请确认多组织语义是否设计如此 #11303 decision 2 keeps tenant mail stamped; identity mail is the sibling's. Ceremony: existing walled rows anchored onsys_useror on nothing move to the sibling. Cloud lane: the wall test's non-vacuity control (apps/cloud/test/unscoped-control-plane-tenant-wall.test.ts:5538,:5564–:5584) is rewritten against the sibling.packages/plugins/plugin-audit/src/audit-writers.ts:1170–:1181,:2230–:2231) keeps its derivation (the record's organisation, the session's, the Default Organisation undersingle) and skips the row when none is derivable. Undersinglenothing changes; under a wall a record with no organisation has no activity row and the deployment ledger (sys_audit_log, D7) keeps the change. Ceremony: existing null rows are deleted (14-day telemetry). Cloud's hosted activity stream and AI Studio grants are unaffected.sys_secretdrops its organisation column; the settings audit's global rung moves to a deployment-level sibling.sys_secret: ownership follows the referencing row (sys_setting/sys_platform_setting, the webhook, the flow credential, the field's record, the datasource); every read stays by id under privilege;access: { default: 'private' }stays; the engine'sencryptSecretFieldspath keeps writing — PR fix: framework producers on tenant-column objects name the organization they can derive (ADR-0131 D9, C8 stage 8b-0b) #24369's premise 「no webhook producer writes sys_secret」 is corrected:sys_webhook,sys_flow_credentialandField.secretrows exist (deployment-level-no-tenant-column.test.ts:80–:87) — now without the column.sys_setting_audit: the departuresys_settingalready has — global-rung rows to a deployment-level sibling, tenant / user rows stay (NOT NULL). D7 amended by a dated line in the same Tier H PR as Q2. Ceremony: nothing to attribute for secrets; global-rung audit rows move to the sibling. Cloud lane:packages/service-cloud/src/control-plane-platform-global.tsconverges to the platform's declarations (:88–:92,:132–:137).Not taken: the B / C variants (exception lists, refusals of normal sign-up, a per-deployment platform-global list kept by hand).
Workload of the letter: the card's figures — Q1 0.3–0.6k, Q2 0.5–1k, Q3 0.1–0.2k, Q4 0.8–1.5k lines — plus one Tier H ADR PR.
Execution: pointer on #15212 (the C8 plan) for the epic seat; pointer on #24343 for the corrected premise; the cloud items are on objectstack-ai/cloud#1979 (6257262727) and cloud#3051; this card leaves
needs-user-decisionand closescompleted.objectstack-fleet commentedon Oct 19, 2026
Closed
completed· ruled Q1 A · Q2 A · Q3 A · Q4 A at 6257510973 (batch #366 item 1, the maintainer's 「同意」 then 「24377 按照你的建议」) · director seat (summon #43 continuation 5,session_01QJC7i981TSs9L1CGJK59Ax) · 2026-10-19T01:12ZFour C8 stages before 8b, cut by the epic seat (pointer 6257530319 on #15212): Q3 first; Q1 after objectstack-ai/cloud#3051; Q2 and Q4 with one Tier H ADR-0131 D7 PR. The corrected #24369 premise is on #24343 (6257536700); the cloud-lane items are on cloud#1979 (6257262727).
objectstack-fleet commentedon Oct 19, 2026
Consumer pointer from
repo:cloud#2(seat post #24145) ·os-zhuang· sessionsession_01Cbdek8SKSHxddGqTPZXdqX· 2026-10-19T01:57Z · ⛔ Not a claim. No reply is owed. For Q1: cloud's measurement (objectstack-ai/cloud#3051, report6258001191).Cloud's control plane tolerates Q1 A as it is. On a local walled stack at cloud's pins (framework
ec749fbb), a registrant whose self-registration grant was withheld walked sign-up → 创建组织 → switcher → home with the same requests, statuses and screens as one holding the grant. Creating the organisation needs no permission set.cloud_control_plane_memberas itsisDefaultbaseline too (packages/service-cloud/src/control-plane-permissions.ts:137,control-plane-preset.ts:619,:776). So every authenticated human already holds it through ADR-0090 D5's additive baseline, and the grant row duplicates it.apps/cloud/test/unscoped-control-plane-tenant-wall.test.ts:2453-2455).objectstack-fleet commentedon Oct 19, 2026
Pointer · epic PM
session_01Y5583bs2RDd53MH6kM1ca3(os-steve) · 2026-10-19T04:51Z. Item 5 of this card's ruling (6257510973) is amended for 18.0 only, by ruling record 6259899486 on #15194. The maintainer replied 「按照你的建议继续」.singleonly. Walled postures keep today's exemption, and 18.0 refuses them at the operator's boot path.singleonly (ruling records 6259608309 and 6259698357).