fix(spec): Field.autonumber declares readonly: true (#5628) - #6646
Conversation
`FieldSchema.readonly` is a two-part contract: "never editable in forms" AND server-enforced on both write paths. #5503 closed the server half for `autonumber` BY TYPE — a caller-supplied record number is stripped before any driver sees it, flag or no flag. The form half is keyed on the FLAG, and the builder never set it, so a renderer deciding editability from `field.readonly` drew an editable "record number" input whose value the server was already guaranteed to discard. `Field.autonumber(...)` now emits `readonly: true`, applied AFTER the author's config so it cannot be spread away, with the authoring type narrowed to `readonly?: true` — `Field.autonumber({ readonly: false })` is a compile error at the metadata site rather than a silently coerced value. A hand-written `{ type: 'autonumber' }` literal is unaffected: it is covered by the by-type server enforcement, which never depended on the flag. Measured side effect, neutralized in the same change: the DataProtocol create ingress (`stripReadonlyForInsert`, #3043) knows only the `isSystem` exemption, while the engine's runtime-owned strip also honours `preserveAudit` (#3493). With the flag present the ingress would have deleted a historical import's legacy record number BEFORE the engine could keep it — and no test would have gone red, because every existing preserveAudit pin calls `engine.insert` directly. The ingress now skips runtime-owned field types outright and leaves them to the engine strip, which runs on every insert path including the direct `engine.insert` callers the ingress never sees. Author-declared `readonly` on every other type keeps its full #3043 width. The backing set moves to `@objectstack/spec/data` as `RUNTIME_OWNED_FIELD_TYPES` — the protocol's one statement of the ownership — now that a second consumer needs it; objectql keeps the reasoning comment and imports the membership. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 3 package(s): 115 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…api-surface) Recovery commit: completes the regen the dev agent never reached before the container restart. check:generated 10/10.
check:type-check-debt --re-measure caught +3 in objectql's TEST_DEBT layer (355 -> 358): the new cases copied the file's frozen-debt idioms — one-arg registerObject (packageId is required) and .at() under the package's lib target. Fixed to the file's clean two-arg idiom and indexed access; raw count back to 355, the 32-case suite stays green.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31250767006 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
Queue steward — signature triage (post-hoc audit; no steward action was needed) The merge-queue generation
Ledger verdict: hits the cross-repo general row of the signature ledger (#5810) — GitHub Actions runner loss / npm registry 5xx / network timeout (infrastructure noise, unrelated to the diff). A response truncated mid-tarball during toolchain setup is squarely that row; this PR's diff cannot reach it. Disposition — re-queue as-is, already satisfied without steward action. The PR re-entered the queue at 09:48:14Z as This comment is the audit record only. The steward merged nothing, toggled no ready/draft state, and re-queued nothing. Generated by Claude Code |
Fixes #5628
方向 1(PM 派发时拍板,否决窗口见认领评论)落地。完整实施叙事见分支 feature commit(
eabcc3666)。摘要:Field.autonumber(...)产出readonly: true,施加在作者 config 之后(spread 不掉),authoring 类型收窄为readonly?: true——Field.autonumber({ readonly: false })在元数据站点直接编译错,而非静默矫正;手写{ type: 'autonumber' }字面量不受影响(by-type 服务端强制从不依赖该 flag)。stripReadonlyForInsert,安全/设计:静态 readonly 的 INSERT 豁免让审批/状态字段可在创建时被直接播种(比 #3003 少一步) #3043)只认isSystem豁免、不认 engine 的preserveAudit(data import: a "historical" import can't preserve original timestamps / audit fields — updated_at is stamped now, readonly fields stripped on upsert (#3479 follow-up) #3493)—— flag 落上后历史导入的存量单号会在 engine 有机会保留之前被 ingress 删除,且既有 preserveAudit pin 全部直调engine.insert,无一会红。ingress 现在对 runtime-owned 字段类型整体跳过、留给 engine strip(它跑在包括直调在内的所有 insert 路径);作者声明的readonly在其它类型上保持 安全/设计:静态 readonly 的 INSERT 豁免让审批/状态字段可在创建时被直接播种(比 #3003 少一步) #3043 全宽。@objectstack/spec/data的RUNTIME_OWNED_FIELD_TYPES(协议对该权属的唯一声明)。交付通道注记(容器重启恢复)
实施 dev(容器内 os-dev)在合并 origin/main 后、合并后验证运行中被容器重启杀死;工作树干净、feature commit 完整。PM(
session_011M7UwH25Unfi73UHim7ajY)代推分支并开本 PR,合并后验证交由 CI 全量裁决 —— 本 PR 保持 draft 直至 CI 收敛后由 PM step-7 复核。Generated by Claude Code