Skip to content

controlled_by_parent: per-object opt-in so read on the master is enough to insert and update the detail under the detail's own grants (ownerMayEdit covers owner update only; HotCRM Order to Cash blocked) #33250

Closed

Description

@objectstack-fleet

repo:hotcrm#1 · huangyiirene · session_01J8VVAnPkgK6CWrJa6t7Fk5

Ask

A per-object opt-in under sharing.controlledByParent (name is the platform's call, e.g. masterReadSuffices: true) that changes the detail write gate from "EDIT on the master" to "READ on the master". The insert or update is then judged by the detail object's own grants (object CRUD, FLS, its row-level security), exactly as an unrelated object would be. Default stays as today (ADR-0055 §3: master EDIT required), so no existing app changes behaviour.

Why (measured on @objectstack 18.4.0)

HotCRM Order to Cash (hotcrm#2315, draft PR hotcrm#2418) hangs detail objects off a contract: a collection plan with plan steps, invoices, payments. The people who write those rows do not edit the contract, by design:

  • sales_rep reads the contract and must add collection-plan steps: insert answers 403 (master not editable).
  • ar_accountant reads the contract and must record an invoice: insert answers 403, even with Modify All on the invoice object.

sharing.controlledByParent.ownerMayEdit (ADR-0055 §3 amendment) does not cover this. It lets the owner of an existing detail row update it. It does not admit an insert, and it does not admit a non-owner who holds the detail grant.

The workarounds are each worse: dropping controlled_by_parent loses read inheritance from the contract; granting reps and finance EDIT on contracts hands them the commercial terms.

Acceptance

  1. Spec: the opt-in parses on an object that declares controlled_by_parent; lint refuses it elsewhere (as ownerMayEdit does).
  2. With the opt-in, a principal who reads the master and holds create on the detail inserts a detail row; one who holds update on the detail updates it. Without read on the master, both still answer 403.
  3. Without the opt-in, behaviour is byte-identical to 18.4.0 (existing controlled_by_parent tests unchanged).
  4. The master check member on the security service answers the opt-in the same way the write path does.
  5. Docs and the authoring skills describe when to use it (detail rows written by people who only read the master: plans, invoices, payments, activity-like rows).

Ruling

Irene, 2026-11-14T14:45Z, O2C phase 1a Q1 = A: the platform takes this, priority p1; hotcrm changes nothing and hotcrm#2418 is Blocked-by this card.

Activity

  1. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: first touch → domain:spec (lead) · p1 · pm:queue · 2026-11-14T15:52Z

    ⛔ Not a claim.

    Area: A2 for the server half (the detail write gate in plugin-security), leased to domain:server#5 in the pilot

    • Grade p1: ruled on the card (Irene, 14:45Z, O2C phase 1a Q1 = A: the platform takes this, p1). HotCRM's Order to Cash is blocked: reps and AR cannot insert detail rows they must own (hotcrm#2418 is Blocked-by this card).
    • Lane: a new authorable key under sharing.controlledByParent is a spec contract widening (Clause-②: yes), plus its lint, so domain:spec leads. The write-gate change is plugin-security (server, pilot area A2). Either one PR carries both, written by spec and checked by A2, or spec lands the key and A2 lands the gate right after. domain:spec#1 and domain:server#5 agree which.
    • Permission boundary: this is an opt-in the app author declares. With it, a principal who only reads the master can insert or update detail rows under the detail's own grants (CRUD, FLS, RLS). No outside or lower-privileged actor gains a grant the author did not declare, so it is graded as a feature, not a security finding. Acceptance 2 and 3 (no master read → 403; no opt-in → byte-identical) are the pins that keep it so.
  2. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  3. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

    Seat: not dispatched — the ask re-opens a recorded maintainer ruling → needs-user-decision · 2026-11-14T15:54Z

    ⛔ Not a claim. The lane takes this card as soon as the door is ruled.

    • What the card asks: a per-object opt-in under sharing.controlledByParent with which read on the master suffices and the detail's own grants (CRUD, FLS, RLS) decide insert and update for everyone.
    • Governing text: docs/adr/0055-master-detail-controlled-by-parent.md, Amendment 2026-10-26 (controlled_by_parent: the owner of a detail row cannot update their own row when they only read the master — no equivalent of the "activity owner always has full access" rule (hotclm obligations today, HotCRM tasks next) #25760), maintainer ruling of record 6398566164 (「同意」 2026-10-25), verbatim: "Option 2 (writeRequires: 'read', the detail's own object permission and RLS deciding the write for everyone) was not chosen: it opens the write to everyone the detail's own grants admit, a wider door than the need." Same amendment, "What is out of scope": "So the leg does not reach: an insert … Each is a widening of its own, to be ruled on its own evidence; none is implied by this one."
    • So: this card is that rejected option 2 (as an opt-in), plus the insert widening the ADR says must be ruled on its own evidence. The ruling quoted in the body (O2C phase 1a Q1 = A, 14:45Z) decides that the platform takes HotCRM's need at p1; it does not cite or lift 6398566164. The evidence here is new (inserts by a rep who does not own the contract; an AR accountant who is neither owner nor master editor), so the question is legitimately open again, but it is a write-gate (permission boundary) door and an ADR-0055 amendment, both the maintainer's.
    • Options to rule (the seat recommends after the evaluation; four-axis analysis follows on this card):
      • A — as filed: the opt-in admits insert and update by the detail's own grants with master read (option 2 by opt-in). Covers both measured roles.
      • B — narrower: extend the existing ownerMayEdit leg to an insert whose owner_id is the caller (with master read), keeping update owner-only. Covers the rep's plan steps and the accountant's own invoices; does not admit a non-owner update (e.g. Modify All on the invoice).
      • C — no platform change: HotCRM models these rows outside controlled_by_parent (lookup + its own RLS).
    • State: pm:queue → needs-user-decision. hotcrm#2418 stays Blocked-by this card. Whichever door is ruled, the spec seat dispatches one PR carrying the key, its parse and lint refusal, and the gate leg (D5: a key lands with its first reader), with domain:server#5 (pilot area A2) checking the plugin-security half, then the ADR-0055 amendment as its own domain:skills card.
  4. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  5. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

    #33250 决策分析:主从子记录的写权限开关开多大

    写于 2026-11-14,Fable 评估线程。只读评估,未改任何仓库。读数基线:objectstack main 47a3b95,hotcrm#2418 头 024d1ff(@objectstack/* 18.4.0)。

    一句话问题

    销售和应收会计能看合同、按设计不能改合同,但他们要在合同下面建回款节点、录发票和回款。今天平台一律回答 403,HotCRM「订单与回款」模块因此卡住(hotcrm#2418 Blocked-by 本卡,Irene 2026-11-14 14:45Z 定为 p1)。

    Governing text

    • docs/adr/0055-master-detail-controlled-by-parent.md §3:写子记录要求对主记录有编辑权。
    • 同文件 Amendment 2026-10-26(controlled_by_parent: the owner of a detail row cannot update their own row when they only read the master — no equivalent of the "activity owner always has full access" rule (hotclm obligations today, HotCRM tasks next) #25760,裁决 6398566164「同意」):只加了 ownerMayEdit(子记录所有者凭主记录读权限按 id 修改自己的行)。原文:「Option 2 (writeRequires: 'read' …) was not chosen: it opens the write to everyone the detail's own grants admit, a wider door than the need.」以及「So the leg does not reach: an insert … Each is a widening of its own, to be ruled on its own evidence; none is implied by this one.」
    • 本卡要的正是当时没选的方案 2(做成按对象开关),外加新增。当时的需求只有「所有者改自己的行」(hotclm 履约义务);今天的证据是新的,所以问题可以重开,但它是权限边界,也是 ADR-0055 修订,归维护者裁。
    • 应用仓规则(pm-dispatch decision-analysis.md〈应用仓推荐序特例〉):阻塞源是平台缺口时不荐应用绕行。

    是否改协议:是。sharing.controlledByParent 下新增一个可声明的键(Clause-②: yes,widening)。

    前提与读数(可复核)

    前提 读数 复核方式
    两个角色的新增被拒 18.4.0:na.rep 建回款节点 403;fin.ar 录发票 403,子对象上的 Modify All 不起作用 hotcrm#2418 正文 Acceptance notes 前两行
    两个角色都不改合同 sales_rep 与 ar_accountant 对 crm_contract 都是 allowEdit: false git show pr-2418:src/sales/profiles/sales-rep.profile.ts(第 67 行)、ar-accountant.profile.ts
    设计授权包含删除 sales_rep 对 crm_billing_milestone 是增改删全开;ar_accountant 对发票、回款是增改删加 Modify All 同上两个文件(sales-rep 第 117 行)
    写门今天对删除也问主记录编辑权 masterGateCoversOperation 覆盖 update 与 delete packages/plugins/plugin-security/src/platform-ownership-policies.ts
    所有者在写门之后才盖章 写门在步骤 2.8,所有者盖章在步骤 3.5 security-plugin-cbp-write-gate.ts 第 593 至 598 行的注释
    没有别的卡提过同一件事 搜 writeRequires:2 条(本卡与 #25760);搜 ownerMayEdit:19 条,无一条要求放开新增或非所有者修改 gh api -X GET search/issues -f q='is:issue repo:objectstack-ai/objectstack writeRequires'

    未实测、属推断的两点:删除被拒(hotcrm#2418 只实测了新增;由写门代码与 ADR §3 可确定);方案 B 下非所有者修改被拒(由写门代码推断)。

    选项、代价与工作量

    选项 做什么 客户能感到的后果 工作量
    A+(推荐) 开关覆盖新增、修改、删除 子对象声明开关后,写门对主记录的提问从「能否编辑」换成「能否读」;之后照常由子对象自己的对象权限、字段权限、行级策略决定。默认不变 HotCRM 的设计授权全部兑现:销售建、改、删回款节点,财务录、改、删发票和回款 spec 车道,1 个 PR;先例 #26005(ownerMayEdit,+1432/−18,34 个文件,2026-10-26 当天开当天合)。另有 ADR-0055 修订与文档技能卡
    A 如卡所提,只含新增和修改 同上,但删除仍问主记录编辑权 销售删不掉建错的回款节点,财务删不掉录错的回款(回款没有「作废」状态),只能找管理员。两份权限集里的删除授权成为摆设 同 A+,略少几条删除路径的测试
    B 把 ownerMayEdit 延伸到「所有者是自己的新增」 创建者能新增、能改自己的行;其余照旧 两个实测的新增能过。但同事接手合同后改不了前任建的节点;会计乙改不了会计甲或管理员导入的发票;财务的 Modify All 仍然无效 spec 车道,1 个 PR;所有者新增分支无先例,且写门运行时所有者还没盖章,比 #26005 更绕
    C 平台不改 HotCRM 三个对象放弃随合同控制,改用普通引用加自己的行级策略 失去「能看合同就能看它的发票」的继承;要靠钩子把合同的可见范围抄到子记录上并保持同步 hotcrm 车道,三个对象加钩子与共享重做;无先例;违反应用仓不绕行规则,也与 Irene 的 Q1 = A 相反

    业务含义直译

    • A+:等于 Salesforce 主从关系字段上自带的「共享设置」选「只读」那一档:对主记录至少有读权限的人,可以新增、修改、删除子记录,前提是他在子对象上有相应权限。
    • A:同一档位,但把删除单独留给能改合同的人。
    • B:等于「谁建的谁管」。适合任务、履约义务这类一人一行的东西,不适合发票、回款这类团队共管的账。
    • C:等于告诉应用「主从关系只管读,写的事自己想办法」。

    四轴论证

    ① 项目长远合理性(领起)。 两年后这块能力在平台上应该是两个成熟平台都有的两件东西:一是关系级的档位「写子记录至少要对主记录有读权限还是编辑权限」(Salesforce Master-Detail 字段的 Sharing Setting: Read Only / Read/Write,覆盖增改删);二是「所有者始终能改自己的行」(Salesforce 活动所有者规则,已由 ownerMayEdit 落地)。A+ 就是第一件,写门里只是把一个问题换了答案。B 在主流平台上没有对应物,它在写门里再开第三条特例分支(所有者修改、所有者新增、空主记录所有者规则),而且下一个同形模块 PSA(hotcrm#2316,工时与费用挂在项目下)一碰到非所有者修改就得再修订一次 ADR。A 把删除留在外面有一个长期代价:以后再把删除并进同一个键,等于悄悄放宽所有已声明该键的应用;不并就得再加一个键。所以动词范围应当这次一次定清。另外 A+ 的表达力严格大于 B:想要「只有创建者能改」的应用,打开开关再写一条所有者行级策略即可(策略是「与」上去的,会照写的生效);反过来 B 表达不了团队共管。

    ② 实际业务拉动。 拉动是实测的:18.4.0 上两个角色两次 403,一个 p1 模块整体卡住。10-26 那次说方案 2「比需求宽」,当时的需求确实只有所有者修改。今天的需求变了:新增的人不是主记录编辑者,修改的人也不一定是创建者(财务团队、导入、接手)。HotCRM 的权限集本来就给了销售和财务删除权,这也是拉动,只是没有实测 403。

    ③ 防 AI 写错。 B 留下一类很隐蔽的错:AI 写的应用用单个用户测试时全部通过(创建者就是所有者),到真实团队里同事一改就 403,而且权限集上写着的 Modify All 不兑现。A 不含删除时,AI 声明了删除授权加开关,删除仍被拒,同样是「声明了、运行时不兑现」。A+ 的规则是一句话:开了开关,主记录只问读,其余全看子对象自己的授权。出错时看到的是响亮的 403,不是静默放行。A+ 的风险是 AI 为了让 403 消失随手打开开关;约束在于它是显式声明、键是闭合的、放错对象解析即拒,写权限仍以子对象上逐权限集声明的增改删授权为上限。

    ④ 创业阶段不扩散。 A+、A、B 都是新增一个键或一处语义。B 若复用 ownerMayEdit 会悄悄改变 hotclm 已声明对象的行为,若另起键则以后大概率还要为非所有者修改再加一个。A 以后要补删除也要再动一次。A+ 一个键付清。不新增 lint 门禁,只沿用「放错对象解析即拒」。

    自检:只看①选 A+;②③④ 是否翻转:否。

    推荐

    推荐 A+。不取 A:把删除留到以后,等于将来悄悄放宽一个已发布的开关,或者再加一个键。不取 B:它只盖住今天实测的两次新增,盖不住财务团队共管和接手场景,且没有主流对应物。不取 C:是应用绕行平台缺口,并丢掉读继承。

    置信缺口,如实列出:

    1. 删除被拒与 B 下非所有者修改被拒是从写门代码推断的,没有在 hotcrm#2418 上实测。
    2. Salesforce 的档位是凭产品知识引用的,检索结果里能看到同样措辞的帮助页与社区页,但没有逐页打开官方文档核对。
    3. 代办写入(on-behalf-of)在开关下怎么判,我建议两趟各按同一问题判(各自读主记录加各自的子对象授权),但没有通读 ADR-0090 D10,需要契约复核确认。

    裁后执行(若裁 A+)

    1. spec 席位 1 派一个 PR,键、解析拒绝、写门分支一起落(D5:键随第一个读者落地)。键名由 spec 席位定,放在 sharing.controlledByParent 下,闭合形状,非 controlled_by_parent 对象上声明即解析拒绝。
    2. 写门:对声明了开关的子对象,凡是今天问「主记录可编辑」的地方(按 id 新增修改删除、批量新增、按条件写、链上每一跳、把行挪到另一个主记录下)统一改问「主记录可读」,读的答案取自读门本身。子对象自己的对象级授权、字段权限、行级策略照旧先判。未声明的对象逐字节不变。安全服务的 checkControlledByParentWrite 给出同样答案。
    3. 建议一并定下:同时声明新开关与 ownerMayEdit 时解析拒绝并提示删掉后者(新开关已包含它);现有 lint 提示把新开关也认作让所有者策略生效的声明。不新增 lint 规则。
    4. ADR-0055 再修订一段,写明本次裁决对已声明开关的对象取代了 2026-10-26 修订里「Option 2's door stays closed」一句;受管面,PR 时请维护者过目。文档与技能另开 domain:skills 卡。
    5. 平台发版后,hotcrm#2418 给三个对象打开开关、升级 pin、复测销售与财务的增改删,再落地。

    四棱块

    os-decision-facets
    ① 项目长远合理性:A+ 缩小特例,一个问题换一个答案,对齐主流平台的关系级档位;B 扩大特例,再开一条所有者分支且下一个模块还要修订。
    ② 实际业务拉动:今天撞上的是 HotCRM 订单与回款(18.4.0 实测销售与财务新增各 403,p1);删除与非所有者修改由设计授权与写门代码推断,未实测。
    ③ 防 AI 犯错:A+ 是闭合键加解析拒绝,规则一句话,出错是响亮 403;B 与不含删除的 A 都会留下「声明了授权、运行时不兑现」的形状。
    ④ 创业阶段不扩散:A+ 一个键付清;A 与 B 都预留了下一次加键或悄悄放宽。不新增门禁。
    Prior rulings read: controlled_by_parent / writeRequires / ownerMayEdit → 38 / 2 / 19 hits; ADR-0055 Amendment 2026-10-26(裁决 6398566164,经 ADR 原文与 #25760 正文读到,裁决评论本身未单独打开); thread: none

  6. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

    Ruled: A+ · needs-user-decision → pm:queue · 2026-11-14T16:23Z

    • Ruling-ref: the maintainer (Leehom) chose option A+ 「读主记录即可增改删」 at 2026-11-14T16:21:29Z on the decision 「裁定 controlled_by_parent: per-object opt-in so read on the master is enough to insert and update the detail under the detail's own grants (ownerMayEdit covers owner update only; HotCRM Order to Cash blocked) #33250 主从子记录的写权限开关开多大」 in this account's fleet project (Fable evaluation thread; analysis = comment 6880272329). Option text verbatim: "开关打开后,能读主记录的人按子对象自己的授权新增、修改、删除,默认不变。HotCRM 的设计授权全部兑现。"
    • What it rules: a per-object opt-in under sharing.controlledByParent; with it, the master question in the write gate is read instead of edit for insert, update and delete; the detail's own object grants, FLS and RLS decide as before; an object that does not declare it is byte-identical. For opted-in objects it lifts the 2026-10-26 amendment's "Option 2 … was not chosen" line of ruling 6398566164.
    • Left to the seat (not ruled by the tap): the key name; the paths the swap covers (batch insert, predicate writes, chain hops, re-point); its relation to ownerMayEdit; the lint arm. The dispatch order carries them as seat calls under the veto window.
    • Next: the spec seat dispatches one PR (key + parse refusal + gate + security-service member + docs, D5), contract review before enqueue; the ADR-0055 amendment and the skills/** teaching are domain:skills carriers filed at landing. domain:server#5 (pilot area A2) is told and checks the plugin-security half.
  7. added
    pm:queueReady for the PM dispatch loop
    and removed
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    on Nov 14, 2026
  8. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

    Claim: PM loop round 1 · 2026-11-14T16:25Z
    Session: session_01FEkQ5mswnWm181XgYcCUag (seat); card thread session_01FEkQ5mmtu9Aih9i3fVmmve is the dev
    Account: os-project-manager
    Branch: claude/project-thread-gkpv0o (the card thread's designated branch; the platform lets a thread push only that branch, so not claude/issue-33250-*)
    Worktree: objectstack-issue-33250
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/data/object.zod.ts (+ test, object.form.ts, regenerated schema/references and packages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts), packages/spec/src/contracts/security-service.ts; packages/plugins/plugin-security/src/security-plugin-cbp-write-gate.ts, security-plugin-cbp-relation.ts, platform-ownership-policies.ts, explain-engine.ts and the wiring that implements checkControlledByParentWrite, with tests; packages/lint/src/validate-security-posture.ts, rule-explanations.ts and tests; packages/metadata-core/src/object-schema-fls-references.ts only if it enumerates the controlledByParent keys; hand-written docs of the controlled_by_parent write rule under content/docs/**; ui/packages/app-shell/src/views/metadata-admin/metadata-form-zh-cn.ts only if the form needs the label; root .changeset/. ⛔ docs/adr/** and skills/** are carriers (stop on breach; explain in the report)
    Container & model: L, mode:thread, model: default tier (Clause-② widening built at the judgment tier; contract review at CONTRACT_REVIEW_TIER before enqueue, MANDATORY path: Clause-② yes), effort: high
    Clause-②: yes
    Metadata-protocol: added
    Responsibility: n/a — not a defect card (a capability the maintainer ruled; the gate's own code in plugin-security produces today's 403, objectstack)
    Thread-read: 6880588685
    Premise: holds at origin/main 24f2f401: sharing.controlledByParent declares only ownerMayEdit (object.zod.ts), and the gate asks master edit for insert and delete. Ruling-ref: the maintainer (Leehom) chose A+ 「读主记录即可增改删」 at 2026-11-14T16:21:29Z (record 6880588685). Order: /mnt/project-files/pm-orders/spec/order-33250.md (project share). Seat call under the veto window: key writeRequiresMasterRead (precedent: Salesforce's master-detail attribute of the same name and meaning).
    Serial constraints cleared: open PRs touching plugin-security are #33303 and #33290 (security-plugin-authz-middleware.ts, security-plugin.test.ts) and #33245 (security-plugin-start-hooks.ts), none on the cbp gate, relation or ownership files; no open PR touches object.zod.ts, security-service.ts or validate-security-posture.ts. If the dev must edit security-plugin.test.ts, it merges main once after #33303 lands. Server pilot area A2 (domain:server#5) checks the plugin-security half on the PR.

  9. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  10. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mmtu9Aih9i3fVmmve
    os-dev-report
    {
    "issue": 33250,
    "status": "done",
    "branch": "claude/project-thread-gkpv0o",
    "pr": "#33369",
    "session": "session_01FEkQ5mmtu9Aih9i3fVmmve",
    "premise_still_valid": true,
    "summary": "Added sharing.controlledByParent.writeRequiresMasterRead (ruling A+). With it the ADR-0055 §3 write gate asks READ on the master instead of EDIT for insert (single and batch), by-id and predicate update/delete, re-point and emptying, per chain hop (an opted hop asks read and ends the walk; a non-opted hop keeps edit); the detail's own grants, RLS and FLS then decide. checkControlledByParentWrite and os explain agree with the gate; the lint owner-policy arm stands down on an opted detail; ObjectSchema refuses the key off a controlled_by_parent detail and beside ownerMayEdit:true (fix: delete ownerMayEdit). Undeclared objects are unchanged (full plugin-security suite green). Deviation from the order's wording: a by-id update/delete by someone who cannot read the master answers 404 RECORD_NOT_FOUND (read door first, #7505 hidden-row rule), not 403; insert and re-point answer 403; checkControlledByParentWrite gives deny legs for all.",
    "tests": "Red on main (b4 gate change): plugin-security controlled-by-parent-owner-may-edit.test.ts 21 failed / 22 passed, delete by a master reader = 403 'requires edit access to its master record (no edit permission on master omd_contract)'. Green on b012326: that file 43/43; explain-controlled-by-parent-write.test.ts 87; spec controlled-by-parent-owner-may-edit.test.ts 26; lint validate-security-posture-owner-may-edit.test.ts 40. Full suites: spec 840 files/26844 tests exit 0; plugin-security 267/6641 (45 skipped) exit 0; lint 208/8890 exit 0; platform-objects 122/1957 exit 0 (two echo-decision leaf counts bumped 113->115, 737->738); app-shell metadata-form-i18n.test.ts 22 exit 0. Typecheck exit 0: spec (check:test-typecheck at --max-old-space-size=8192), plugin-security, lint, platform-objects; app-shell tsc not measured (1821 pre-existing errors from unbuilt ui deps, none in metadata-form-zh-cn.ts). Ablations via scripts/ablation-replace.mjs, each 'mutation landed' + 'restored: blob == HEAD': gate read branch (if (false && hopRel.writeRequiresMasterRead)) -> 21 red incl. both checkControlledByParentWrite pins; chainAsksMasterRead -> false -> 3 explain red; lint 'if (detail.masterReadSuffices) continue;' deleted -> 2 red; off-detail parse refusal off -> 4 red; pair refusal off -> 1 red.",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands: 123 families, each run with exit captured before any pipe; --ran reconciliation exit 0: 123 derived, 122 run exit 0, 1 NOT MEASURED (pnpm check:dual-build-cjs-loads exit 3, needs every package dist; CI runs it). Three first-pass prerequisite refusals re-run green: check-plugin-teardown-shape --self-test (exit 3 shallow clone -> fetched pinned fixture 621a487 -> exit 0), spec check:skill-examples (exit 3 -> built client-react -> exit 0), spec check:generated (exit 1 on test-typecheck OOM 'failed without diagnostics' -> heap 8192 -> exit 0). check-i18n-bundles exit 0. CI check-runs on #33369: not yet read (seat holds PR events).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "5 — issue_read get, issue_read get_comments x2, pull_request_read get_files (precedent #26005), pull_request_read get (#33369 read-back); no write tools",
    "api_writes": "3 — git push (3 pushes to claude/project-thread-gkpv0o), relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 43622253252 success), this os-dev-report comment via post-stamped",
    "deviations": [
    "PR body Verification section was mangled at pr_create (shell expanded backticks in my body script): the 'On head' line, package names and gate names are blank and the i18n gate output was pasted in. Everything above ## Verification is intact (read back). Corrected full body: /mnt/project-files/pm-orders/spec/33369-pr-body-corrected.md; os-dev does not PATCH bodies, so the seat rewrites it per seat-post-protocol.md.",
    "Order said refusals 'each answer 403': by-id update/delete without master read answer 404 RECORD_NOT_FOUND (read door precedes the gate; unchanged platform rule). Pinned as such."
    ],
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "class: c · reach: exception: maintainer · ADR-0055 §3 needs an amendment recording writeRequiresMasterRead (ruling A+ lifts ruling 6398566164's 'Option 2 not chosen' line for opted-in objects); docs/adr/** is out of this card · carrier: seat",
    "class: c · reach: exception: maintainer · skills/** controlled_by_parent authoring guidance should teach writeRequiresMasterRead (PR body carries Skills-docs: follow-up owed) · carrier: seat",
    "class: c · reach: contract review · denial legs reused (object_permission / row_level_security) for the read question to avoid widening the closed ControlledByParentWriteDenialLeg union; the seat's contract review should confirm against D10 and the delegation rule implemented (agent and delegator must each read the master) · carrier: seat"
    ]
    }


    Generated by Claude Code

  11. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

    ACCEPT → PR #33369 comment 6882030929 · 2026-11-14T18:13Z · ready + auto-merge armed; card closes on merge. Carriers: #33373 (ADR-0055 amendment), #33374 (skills guidance), #33381 (pins and comments).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

domain:specDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions