Skip to content

Studio 在代码定义的包里把「只读」一刀切到所有类型:运行时实测接受其中 15 类的 overlay 写入,同屏还并存「可写」徽标与全部禁用的控件 #4036

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#5768 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-08-06T04:31:30Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


Studio dogfood 时发现(最新 main 5e3c83bd0 + objectui 5a24ad9cb89c,showcase 实例,/_console/studio/com.example.showcase/*)。

先说清楚:「代码定义的包不给在 Studio 里改」很可能是有意的产品策略(提示语「请切换或新建可写软件包后再编辑」读起来就是一条策略)。本条真正想请判定的是:这条策略与运行时的实际能力、以及 Studio 自己在同一屏上的其它显示,三者对不上。

三处证据

1)运行时明确接受其中 15 类的 overlay 写入

对同一个包 com.example.showcase:

PUT /api/v1/meta/object/showcase_account   → 403
{"error":"[NOT_OVERRIDABLE] 'object' is not allowOrgOverride in the registry.
  Overlay-allowed: view, page, dashboard, app, action, report, dataset, flow,
  translation, email_template, book, permission, position, tool, skill. ...",
 "code":"NOT_OVERRIDABLE"}

object 被正确挡住了 —— 服务端闸门是真的,不是只在 UI 上禁用(这点值得肯定,我专门验了)。但同一条报错里,服务端自己列出了 15 个 overlay-allowed 类型。挑其中一个实测:

PUT /api/v1/meta/view/showcase_task.in_progress   → 200
{"success":true,"seq":1,"state":"active",
 "message":"Saved customization overlay (env-wide, state=active) — type=view, name=showcase_task.in_progress [seq=1]"}

写进去了。 (我已把 label 改回 进行中 还原。)

2)Studio 对这 15 类一律禁用

/_console/studio/com.example.showcase/interfaces(视图 / 页面 / 看板 / 应用 —— 全是 overlay-allowed 类型):

{"保存草稿": {"disabled": true, "title": "只读软件包 — 请切换或新建可写软件包后再编辑。"},
 "发布":     {"disabled": true, "title": "只读软件包 — 请切换或新建可写软件包后再编辑。"}}

也就是:运行时接受的写入,Studio 不让你发起。

3)同一屏上并存「可写」徽标与全部禁用的控件

/_console/studio/com.example.showcase/access,权限集 showcase_contributor 的标题行渲染成:

权限集 · showcase_contributor · 安全 · 可写 · 已授权对象 5 · 字段覆盖 3 · 历史
                                      ^^^^

而同一个屏幕上:207 个权限勾选框全部 disabled,发布 也 disabled,理由写着「只读软件包」。一个贴着「可写」的对象,它的每一个控件都是灰的。

顺带同类的一处:数据 → 表单 标签页在这个只读包里显示

布局 · 预览 · 草稿布局 · 含未保存改动

—— 在一个什么都改不了、保存草稿 是灰的包里,声称「含未保存改动」。(该处 [draggable=true] 数量为 0,拖拽本身确实被正确禁用了,所以这纯粹是文案与实际状态不符。)

请判定的问题

A. 代码定义的包里,overlay-allowed 的那 15 类到底该不该在 Studio 里可编辑?

  • 若该:interfaces / access 的禁用是过度保守 —— Studio 藏起了运行时真的提供的能力(AGENTS.md Prime Directive [WIP] Enhance every detail of the designer #10 反过来的那一面:declared ≠ enforced 的镜像,这里是 delivered ≠ offered)。禁用应当按类型判定,而不是按包一刀切。
  • 若不该(策略优先,避免代码包与运行时 overlay 产生漂移):那么 2 是对的,需要修的是 1 和 3 —— 服务端为什么还接受这条写入?以及「可写」徽标不该出现在这里。

B. 无论 A 怎么判,3 都是要改的:「可写」徽标和「含未保存改动」在全禁用的界面上是误导性 affordance。

复现

1. pnpm dev(showcase),登录 admin@objectos.ai / admin123
2. 打开 /_console/studio/com.example.showcase/interfaces
   → 保存草稿 / 发布 均为 disabled,title「只读软件包 — 请切换或新建可写软件包后再编辑。」
3. 浏览器控制台:
   fetch('/api/v1/meta/view/showcase_task.in_progress',{credentials:'include'})
     .then(r=>r.json()).then(v=>{v.label='PROBE';
       return fetch('/api/v1/meta/view/showcase_task.in_progress',
         {method:'PUT',credentials:'include',headers:{'content-type':'application/json'},
          body:JSON.stringify(v)}).then(r=>r.status)})
   → 200(记得改回 '进行中')
4. 打开 /_console/studio/com.example.showcase/access,选 showcase_contributor
   → 标题行有「可写」徽标,下方 207 个勾选框全部 disabled

本轮一并验过、确认没问题的(避免后续重复排查)

  • Studio 四个面(数据 / 自动化 / 界面 / 权限)都正常渲染真实内容:对象列表 23 个、flow 列表带启用状态、导航树、权限矩阵 23×CRUD + 字段覆盖。
  • 数据 的内层页签(记录 / 表单 / 验证 / 钩子 / 操作 / API / 设置)可切换;表单设计器正确渲染 12 个字段及其类型(TEXT/SELECT/CURRENCY/URL/LOCATION/DATE/JSON)。
  • 打开条目时的 GET /api/v1/meta/<type>/<name>?state=draft → 404 {"code":"NO_DRAFT"} 是有意设计(错误码明确、UI 正确降级),不是缺陷。唯一可议的是不存在的条目也返回同一个 NO_DRAFT,无法区分「没有草稿」与「这个条目根本不存在」—— 单独提一句,不作为本条主张。
  • 服务端对 object 的 NOT_OVERRIDABLE 拦截是真闸门,报错信息给了可执行的下一步(OS_METADATA_WRITABLE)。

影响面

Studio 编辑面。不影响已发布应用的运行时行为,也不影响数据。属于「能力可达性 + 界面自洽性」问题。

Activity

  1. changed the title [-]Studio 在代码定义的包里把「只读」一刀切到所有类型:运行时实测接受其中 15 类的 overlay 写入,同屏还并存「可写」徽标与全部禁用的控件[/-] [+]Studio 在代码定义的包里把「只读」一刀切到所有类型:运行时实测接受其中 15 类的 overlay 写入,同屏还并存「可写」徽标与全部禁用的控件[/+] on Aug 10, 2026
  2. self-assigned this
    on Aug 11, 2026
  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    CLAIM(B 半边) — session session_017Qqyix2QcnpUC9XeYVDzx3(objectui whole-repo PM seat),dispatching now。Branch: claude/issue-4036-studio-readonly-affordances。

    执行依据:源线程 objectstack#5768 的维护者批复半裁(2026-08-06 两条):

    • B 半边放行 objectui 先行(本次派发):「可写」徽标与「含未保存改动」等误导 affordance 修正——显示侧收敛到当前实际策略(包级一刀切只读)。B 落地不影响 A 的任一走向。
    • A 半边仍待拍(不派、不裁):15 类该不该 Studio 可编辑属逐类产品判定,且 A2 已定方向为「Studio 维持只读、收紧服务端注册表对照 ADR-0005 修正案」(permission/flow/tool/skill 四类明文禁止 per-org override,注册表与 Accepted ADR 冲突须归一)——该复核归 spec/engine 车道。本卡 A 半入决策收件箱等待位,勿催。

    本卡在 B 落地后保持 open(A 半为剩余),PR 用 Part of #4036。

    Surface:Studio 编辑面的徽标/文案渲染(app-shell metadata-admin / studio 组件)+ 测试;metadata-admin 区域当前无在飞持有者。


    Generated by Claude Code

  4. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    State correction: pm:dispatched → needs-user-decision (restored) — theme PM for objectstack#8668 (Seat B), session session_018x7oGZF6qqdUD1Km4e9n5a.

    The B half is delivered: PR #4341 merged 2026-08-11T17:57Z, converging the misleading display — the access header badge that advertised "writable" above 207 disabled checkboxes, and the Data → Form caption that claimed unsaved changes on every clean layout tab — onto the package-level read-only policy the 2026-08-06 half-ruling preserved. No gate was loosened, by design.

    The A half was never dispatched and was explicitly parked in your inbox by the dispatching PM's own claim comment (「本卡 A 半入决策收件箱等待位,勿催」). The label was overwritten by the B-half dispatch and not restored at merge; this puts it back.

    The A-half question is unchanged: whether the 15 overlay-allowed types become individually editable in Studio (A1), against the ADR-0005 amendment which forbids per-org override for permission / flow / tool / skill on silent-privilege-drift grounds. Note the A2 half of that ruling has since landed server-side (objectstack#6483) — it closed the overlay door and kept the runtime-create door open — and objectui has already tracked it through the type tier (#4446 / PR #4519) and the artifact tier (#4518 / PR #4525). So A1 is now the only open piece, and it is the "等真实定制拉动再议" one.

    Full orphan re-verification: objectstack#8668.


    Generated by Claude Code

  5. os-zhuang commented on Aug 15, 2026

    @os-zhuang
    ContributorAuthor

    Maintainer ruling (delegated adjudication; delegation 2026-08-15 verbatim 「决策你直接帮我做」, batch confirmed 「同意」)

    Ruled (A1): no. The package-level read-only policy in Studio is the product answer, not over-conservatism. Card closed.

    Both other halves already landed: the B half (misleading affordances — the "writable" badge above 207 disabled checkboxes, the "unsaved changes" caption on a clean read-only tab) merged as PR #4341; the A2 half (server registry tightened against the ADR-0005 amendment, closing per-org override for permission/flow/tool/skill) landed as objectstack#6483 and was tracked through objectui in #4446/PR #4519 and #4518/PR #4525.

    On A1 — whether the remaining overlay-allowed types become individually editable in Studio inside a code-defined package:

    1. Platform coherence — "a code-defined package is read-only in Studio; customize in a writable package" is one rule. Per-type editability is a matrix that must be maintained against a registry that moves.
    2. Measured pull — zero. No user or agent has asked to overlay-edit a code package in place, and the refusal message already names the working path.
    3. AI error-resistance — a per-type matrix teaches AI authors two different rule sets depending on which package they are in; one rule is learnable.
    4. Startup scope — the matrix is permanent maintenance surface for an unrequested capability.

    The server's overlay door for the remaining types stays open — that is the API/import power-user channel, and it is deliberate, not a leak. With the B half landed, the UI no longer contradicts itself about it: Studio declines to originate these edits, the API still accepts them, and both statements are now consistent on screen.

    Reopen condition, named: real customization pull — a user or agent that needs in-place overlay editing on a code-defined package and cannot use a writable package.

    Closing as completed.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions