[design] ADR-0104: field runtime value-shape as a first-class contract (spec-owned), typed action handlers, file-as-reference#3412
Open
os-zhuang wants to merge 5 commits into
Conversation
…ract
Design for three coupled gaps with one root cause (the runtime value shape
of a field is nobody's contract):
- D1: spec-owned valueSchemaFor(field) + semantic type classes; converge the
four hand-duplicated type lists (record-validator, import-coerce,
driver-sql, verify); reconcile the three dead value-schema exports; wire
the field-zoo oracle to the contract.
- D2: typed action handlers — declared params validated at REST/MCP dispatch,
typed ctx.params via FieldValue<T>, file params become fileId references.
- D3: file-as-reference — Field.file/image values store sys_file ids instead
of inline {url} blobs, joining the existing lifecycle/GC/authz machinery.
- D4: three-phase rollout (non-breaking convergence -> param enforcement ->
protocol-major file migration).
Generalizes the #3405/#3406 silently-stripped-param incident; relates #3407,
#1878/#1891.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
…get sections Nine named risks (legacy-row stranding, warn-first rollout, unknown-key allowlist, GC freeze during migration, public-posture inventory, sub-key read breakage, external-URL retirement, cross-repo sequencing, spec purity) and the performance budgets/benchmark gates for each phase, so the phase PRs inherit them as acceptance items. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
os-zhuang
marked this pull request as ready for review
July 22, 2026 20:57
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
纯设计文档:新增
docs/adr/0104-field-runtime-value-shape-contract.md(Status: Proposed),不含任何代码改动。背景
#3405 / #3406(内联 lookup 参数的
reference被.strip静默吃掉)修掉的是一个键;这份 ADR 处理它背后的类问题:**字段的运行时值形状(value shape)在整个平台里不是任何人的契约。**代码盘点(全部带 file:line 证据,见 ADR 正文)发现:record-validator.ts(私有MULTI_CAPABLE_TYPES,单值 lookup / file / location 等完全不校验,line 341「opaque payloads」)、import-coerce.ts(6 份自己的类型集合)、driver-sql(JSON_COLUMN_TYPES/NUMERIC_SCALAR_TYPES)、verify/read-coercion.ts(只覆盖 3 个类型)。CurrencyValueSchema说{value,currency},实际存裸 number;LocationCoordinatesSchema说{latitude,longitude},field-zoo 存{lat,lng})——正是 ADR-0078 禁止的「导出但惰性」。type/required/multiple/accept/maxSize)只喂给客户端弹窗;服务端handleActions原样透传reqBody.params(http-dispatcher.ts:3882,MCP 路径同样),沙箱input: unknown,handler 注册签名(ctx: any) => any,现有 handler 全靠裸as强转。{url,name,size}),完全绕开平台已有的sys_file一等文件原语(生命周期 GC、下载鉴权、稳定/files/:fileId解析器)——attachment-lifecycle.ts:27-29自己就写着字段列的引用是 join-row 计数看不见的。后果:无引用完整性、清空字段永久泄漏 blob、非附件文件全部匿名 capability URL、FieldSchema里连accept/maxSize都没有、client.storage.upload()甚至拿不到fileId(complete 响应把它丢了)。决策概要
field-value.zod.ts导出语义类型集合 +valueSchemaFor(field, form)(stored/expanded双形态,把 lookup 展开多态显式命名);「现实优先」原则处理 3 个死 schema(currency 定为裸 number 并墓碑化旧导出等);4 个消费方收敛到同一来源;field-zoo 往返测试反向断言契约,防止契约与 oracle 漂移。defineAction衍生ctx.params的静态类型,registerAction收紧签名;file/image 参数走 fileId + 服务端按sys_file元数据实施accept/maxSize。sys_file的 fileId(multiple → 数组),展开形态为 spec 定义的FileValueSchema(url 派生、永不落库);写路径维护引用行,接入 ADR-0057 既有 tombstone/reap GC;下载鉴权复用 attachments 模型,匿名 URL 变 opt-in;协议 major + 双读窗口 +os migrate回填(外链 URL 进迁移报告,不静默丢弃,遵守 service-automation: update_record reportssuccesswhen written fields are silently stripped — no observability for dropped writes (split from #3356) #3407 纪律)。lookupaction param declare its reference target (#3405) #3406 同姿态:本来就坏的配置从无声坏变有声坏)→ 文件迁移(协议 major,objectui 同步跟进)。备注
🤖 Generated with Claude Code
https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
Generated by Claude Code