## 要拍板什么 readonly 剥离的 **strict/reject 模式**(供 cron/服务端插件在调用点选择「被剥离即响亮失败」而非静默丢列)落在哪一层。#4903(PR #5123)已把可发现性与文档修好,strict 半边因四个候选落点全部触碰 `packages/spec/**`(本车道冻结)而止步。 ## 四个选项(完整两轴分析见 PR #5123 正文,此处摘要) | 选项 | 落点 | 要害 | |:--|:--|:--| | A | `EngineUpdateOptionsSchema`(可序列化 Zod 袋) | 可跨 RPC/REST —— 但**客户端可设置**,在安全相邻路径上开新攻击面;拖动生成物 | | **B(推荐)** | `strictReadonlyWrites` 挂 `WriteObservabilityOptions`(TS 契约,`onFieldsDropped` 所在) | 进程内、客户端不可达、与既有先例同类;抛 `ERR_READONLY_FIELD_REJECTED` 带字段清单 | | C | 部署级开关(env/构造配置) | 全局改行为 —— REST 边界的伪造输入也从剥离变报错,兼容性变更;无法表达「只这一个 cron 要响」 | | D | objectql 包内私加 | ⛔ 实现而不声明 —— PD#12 反形,不建议 | **推荐 B**:两轴同向 —— 唯一让 AI 写的 cron 在**调用点**拿到点名异常(而非绿调用 + 数周后发现 null 列)、又结构性不给客户端碰的方案。若你想覆盖 RPC/VDE 面,那是 A 的独立放宽,应单独定而非顺手带。 ## 两个关联输入(#5123 报告的,同一次拍板可一并看) 1. `ExecutionContext` 无 origin/channel 标记 —— `isSystem` 是唯一信任位,故剥离接缝无法区分伪造 REST body 与可信服务端代码;任何按来源分级的策略都以补这个标记为前提; 2. `onFieldsDropped` 不跨 RPC/VDE 边界(契约已文档化)—— strict 决策应顺带说清远程调用方拿什么。 `Blocked-by: objectstack-ai/objectstack#4903`(PR #5123 合并后动手) 落点:`packages/spec/src/contracts/data-engine.ts` + objectql —— **spec 车道或维护者授权例外**。 --- _Generated by [Claude Code](https://claude.ai/code)_
Activity
os-zhuang commentedon Aug 4, 2026
维护者拍板:取 B
strictReadonlyWrites挂WriteObservabilityOptions(TS 契约,onFieldsDropped所在),被剥离时抛ERR_READONLY_FIELD_REJECTED并带字段清单。不取 A(可序列化 Zod 袋 —— 客户端可设置,在安全相邻路径上开新攻击面)、不取 C(部署级开关 —— 无法表达「只这一个 cron 要响」,且把 REST 边界的伪造输入一并变成报错)、不取 D(实现而不声明,PD#12 反形)。needs-user-decision摘除。依据:B 是唯一让 AI 写的 cron 在调用点拿到点名异常(而不是绿调用 + 数周后发现 null 列)、同时结构性不给客户端碰的方案。若日后要覆盖 RPC/VDE 面,那是 A 的独立放宽,单独立单定,不顺手带进来。
正文列的两个关联输入按此处理:
ExecutionContext的 origin/channel 标记是另一单的前提(任何按来源分级的策略都要它,但 B 不依赖它 —— B 靠的是「进程内可达 / 客户端不可达」这条结构边界);onFieldsDropped不跨 RPC/VDE 的既有文档化事实,在 B 的实现里顺带在契约 TSDoc 上说清远程调用方拿到什么(即:拿不到,strict 是进程内语义)。路由:归 spec 车道(本车道硬禁区)
落点
packages/spec/src/contracts/data-engine.ts+packages/objectql,跨 spec 与 engine。spec 半边(在WriteObservabilityOptions上声明strictReadonlyWrites+ERROR_CODE_LEDGER登记ERR_READONLY_FIELD_REJECTED)是阻塞前置;声明落地后 objectql 的实施半边可由本车道接。Blocked-by: #4903(PR #5123)仍然有效,先确认它已合入再动工。急迫度:低 —— 今天的行为是静默剥离 +
onFieldsDropped可观测(#4903 已修好可发现性),strict 是补一条响亮出路,不是修一个正在漏的洞。spec 车道可排在 #5158 / #5271 / #5239 之后。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
维护者裁定(2026-08-05,经 spec 车道 PM
session_018fxLGQdatPbBUvCgiVxg6D誊写):选 B。strictReadonlyWrites挂WriteObservabilityOptions(TS 契约,进程内、客户端不可达),违例抛ERR_READONLY_FIELD_REJECTED并带字段清单。ExecutionContext无 origin/channel 标记(任何按来源分级的策略以补标记为前提);onFieldsDropped不跨 RPC/VDE 的事实随本单在契约文档里说清远程调用方的行为即可。转入队列并即刻派发。
Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
PM 认领(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05)claude/issue-5126-strict-readonly-writes../objectstack-5126-strict-readonly(基于最新origin/main)domain:spec(维护者裁定即车道授权:落点packages/spec/src/contracts/data-engine.ts+ objectql 剥离接缝)packages/spec/src/contracts/data-engine.ts(WriteObservabilityOptions.strictReadonlyWrites)+ objectql 写路径实现(ERR_READONLY_FIELD_REJECTED带字段清单)+ 契约测试(含「EngineUpdateOptionsSchema不得出现该键」的反 A 蔓延守卫)SYNC_ARCHITECTURE.md的 L3Connector示例不可编译,且写的是 schema 会**拒收**的键名(sourceField/targetField/transform.type: 'custom'/ webhookretryPolicy) #5515、44 个strictUnknownKeyError直接调用点的别名表在 #5013 闸门覆盖之外(实测干净,但无人看守) #5483 及同批RecordHighlightsFielddoes not declarereadonly, so the spec silently strips the key the chip gate reads #5176 文件面互不相交Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
验收通过(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05)—— PR #5610,裁定 B 落地。strictReadonlyWrites挂WriteObservabilityOptions(进程内契约),违例在任何驱动调用之前抛ERR_READONLY_FIELD_REJECTED带全量字段清单,什么都不写;未置位行为逐字节不变(先证红 9 例 → 全绿 1923 例)。ERR_READONLY_FIELD_REJECTED按账本纪律登记于 objectql 名下(抛出方归属),五条准入规则全过;ENGINE_UPDATE_OPTION_KEYS同步放行 + drift pin 更新。onFieldsDropped/ INSERT 惰性。维护者若只要静态半边,回退面很小。authorable-surface.base.json漂移已按check:authorable-surface在--check模式下也会重写authorable-surface.base.json—— 一次纯核验会改工作区,且任何无关 PR 都能因此静默推进删除门的锚点 #5358 还原。落地暂缓等 #5604(main 全仓红)修复后合 main 重跑 → ready → auto-merge。
Generated by Claude Code
Merge origin/main into claude/issue-5503-autonumber-readonly-strip
docs(changeset): record the strictReadonlyWrites superposition (#5503 x
feat(spec,objectql): strictReadonlyWrites —— 剥离即拒绝的进程内出路 (objectstack…
fix(objectql): autonumber 是运行时拥有的字段,写路径不再接受调用者提交的单号 (objectstack-ai#5503
fix(objectql): hydrate formula fields on the write response, not only…
docs(spec): strictReadonlyWrites contract no longer claims INSERT ign…
docs(kernel): align data-engine contract page with the shipped engine…
test(spec): contracts and conversions test titles state each cited de…