docs(adr): ADR-0104 addendum — the file family's stored column holds the bare sys_file id, switched per deployment on the adr-0104-file-references flag (#15041) - #16014
Conversation
…the bare sys_file id, switched per deployment on the adr-0104-file-references flag Records the maintainer's decision-batch #49 item 1 ruling (Option A) as a 2026-09-05 addendum to ADR-0104: the media family's (image / file / avatar / video / audio) single-value physical column is a string column holding the bare sys_file id — the generator's VARCHAR(2048) / table.string is the ruled end-state and the SQL driver moves to it; the encoding switch is per deployment, keyed on the existing adr-0104-file-references sys_migration row and never on a version, with the column move as a further step of `os migrate files-to-references --apply` after zero blocking findings; the dual-encoding window this implies, its invariant (column type and write encoding never disagree on one deployment), the three populations it must hold over, and its end (the first protocol major after the driver lands, with a loud boot refusal for un-moved deployments); the two confidence gaps carried from the measurement stated as gaps with what closes them; and the sequencing behind the driver card. Governed surface (docs/adr). No code, schema, generated artifact or changeset moves with this commit. Anchors are symbol / file anchors only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
One sentence in the ADR's running index of addenda, in the spelling the existing entries use: the 2026-09-05 addendum rules the media family's physical column — a string column holding the bare sys_file id, switched per deployment on the adr-0104-file-references flag, never per version; driver card #15989 implements it. Seat ruling on the report's open question 1 (B). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
|
CI note from the dispatching seat, for the reviewers — the one red check on Generated by Claude Code |
Fixes #15041
Governed
docs/adr/**— reviews requested from os-zhuang and hotlong; a human merges. Draft on purpose: no seat marks it ready, queues it, or arms auto-merge (Prime Directive #14).What this PR is
One new section in
docs/adr/0104-field-runtime-value-shape-contract.md—## Addendum (2026-09-05) — the media family's physical column holds the bare sys_file id, switched per deployment on the adr-0104-file-references flag— placed after the 2026-08-07 errata, in the file's own addendum spelling. No other section of the ADR is edited and nothing is renumbered. No code, no schema, no generated artifact, no changeset. Every source citation in the section is a symbol or file anchor (thecheck:adr-symbol-anchorsgrammar); no line numbers.The ruling it records
Maintainer, decision batch #49 item 1, recorded by the director seat as comment 5551135629 on #15041. Maintainer verbatim: 「15041 应该改为实际 id 保存。选A,其他同意」. The director seat's record of it:
This PR is step 1 only. Step 2 is the driver card #15989; step 3 (the generator pin block) waits on #15989's PR.
The addendum's five points, one line each
FILE_REFERENCE_TYPESmember the single-value physical column is a string column holding the baresys_fileid; the generator'sVARCHAR(2048)/table.stringis the end-state and the driver moves to it (the family leavesJSON_COLUMN_TYPES;isJsonField/formatInput/formatOutputstop treating it as JSON;varcharColumnCharsmirrors the width).multiple: truemedia fields keep the multi-value JSON array column every multi-value field has; the stored VALUE contract does not move — the column is aligned with it, not the other way round.sys_migrationrowadr-0104-file-references(FILE_REFERENCES_MIGRATION_ID; written byos migrate files-to-references --applyonly when backfill and reconciliation report zero blocking findings, or creation-attested for a datastore born empty; read memoized throughisFileReferencesMigrationVerified), never on a version. The column move becomes step 3 of--apply— after reconciliation, before the row is recorded, aborting on any blocking finding — because the fact the flag attests (every cell is an id) is exactly the precondition under which unquoting is lossless.type_mismatchfinding is keyed tofield.multipleonly, so a SINGLE-value JSON-class column (file family, STRUCTURED_JSON_TYPES) on a char/text column is never reported — the column a hand-run generated migration creates today #15771's silent corruption; a bare write into a native json column is a loud 22P02). Three populations the invariant must hold over are named for the driver card: un-flagged upgraders; deployments flagged before step 3 existed (creation-attested stores since 17.0 and earlier--applyruns — they stay on the JSON arm until step 3 runs on them, whatever the row says); stores born after the driver lands. End: the JSON arm leaves in the first protocol major after the driver lands, and from that major a deployment that has not run its own step is refused loudly at boot for its media fields, naming the command.syncSchema/initObjectsstay additive and never retype an existing column (measured); the generator is unchanged.Temporal Conformance (live PG + MySQL)job); the migration sketch is unrehearsed, including the per-dialect statement order (what settles it: a rehearsal on a copy of a real datastore per dialect, the dry run printing every statement, the apply run proving cell-for-cell equality before and after).file/image/avatar/video/audio) as the baresys_fileid in a string column — dropFILE_REFERENCE_TYPESfromJSON_COLUMN_TYPES, per-deployment switch on theadr-0104-file-referencesflag (ruling on #15041, step 2) #15989 (domain:engine,pm:blockedon this addendum) implements the three sections; the generator's pin block labelled 「recorded divergence, NOT coverage」 retires to coverage only in driver-sql: store the file family (file/image/avatar/video/audio) as the baresys_fileid in a string column — dropFILE_REFERENCE_TYPESfromJSON_COLUMN_TYPES, per-deployment switch on theadr-0104-file-referencesflag (ruling on #15041, step 2) #15989's PR; [finding] field-value.zod.ts docblock over FILE_REFERENCE_TYPES still says the stored schema "deliberately admits both until D3 lands" — the classifier has admitted only the sys_file id since D3 wave 2 PR-5a (17.0.0) #15769 and objectui#7699 are adjacent and unblocked.#15989 is blocked on this PR: it carries a
Blocked-by:line on #15041, and this addendum is the step-1 precondition its ruling names.Two things left for the reviewer, deliberately
Changeset
None, deliberately:
docs/adris not a package and nothing publishes from this diff, so theCheck Changesetjob's opt-out applies. Theskip-changesetlabel is applied to this PR — in this repo that label means exactly "a diff that publishes nothing from any released package".Gates
Union run on
ba6967254(git rev-parse --short HEADafter the only commit;git diff HEADempty; diff vs basef7db8f4fdis this one file, +288/−0;TURBO_SCM_BASE=f7db8f4fd pnpm exec turbo ls --affected→ "0 no packages", so no packagetest/typecheckis owed andpnpm lint(repo-wide eslint, CI's run) lints no.md).pnpm check:adr-symbol-anchors— exit 0 — "1956 anchors across 135 records resolve — 206 symbol (187 declaration, 19 literal), 1729 file-level, 21 cross-repo, 6 exempt, 3 continuation. 0 line anchors survive." Baseline before the edit on the same tree: 1942 anchors / 193 symbol — the +14 are this section's anchors, every one resolving as a declaration.pnpm check:adr-anchors— exit 0 (ADR number uniqueness + governed code names its decision).pnpm check:adr-links— exit 0 — "650 relative link destination(s) under docs/adr/ resolve".pnpm check:doc-authoring— exit 0 — "sibling-package prose ids hold the baseline — 829 pinned site(s) across 231 file(s) … no growth, no burn-down unrecorded".pnpm check:nul-bytes— exit 0 — "scanned 7709 text file(s) … no raw ASCII control bytes"; a directgrep -naPover the C0/DEL range on the file: no match; HTML comments in the file: 0.pnpm --filter @objectstack/lint run check:doc-formula-expressions— exit 0 on ba69672 — "22 record-scoped formula example(s) across 428 files / 1371 TS blocks judged clean by @objectstack/formula". The first attempt exited 3PREREQUISITE NOT MET(@objectstack/formula/@objectstack/lintnot built) — not a measurement; both were built under the shared verification lock (os-verify-lock: VERDICT command-exit 0 · held the lock 126s · waited 0s) and the gate rerun.pnpm check:doc-anchors— exit 0 (313 fragment links across 410 sources resolve; its corpus iscontent/**, so it reads nothing in this diff — run because the dispatch named it).pnpm check:docs-single-h1— exit 0, same corpus.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran— "17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED": the ten families above plus ci-filter-parity, closing-keyword-parity (+ self-test), comment-mask-corpus (6031 files, 0 disagree), cross-package-test-inputs, driver-memory-census, pm-governed-merges, refd-timer-probe, watch-hint-literal and the two ADR self-tests — all exit 0.node scripts/pm/check-governed-merges.mjs --test docs/adr/0104-field-runtime-value-shape-contract.md→ "GOVERNED — a human merge is the review record for this PR" (exit 3): the expected verdict for this PR, not a failure.content/docs/releases/untouched (0 paths). No.changeset/*.md.维护者速读(草稿)
改了什么 — 只改一份 ADR:
docs/adr/0104-field-runtime-value-shape-contract.md末尾新增一节 2026-09-05 addendum(288 行,纯新增),把决策批次 #49 第 1 项(选 A)落成记录:file/image/avatar/video/audio的物理列是存裸sys_fileid 的字符串列(生成器的VARCHAR(2048)即终态,驱动侧改);切换按部署、以现有adr-0104-file-references标志为键,不按版本;写明双编码窗口、其不变量、三类部署与终止条件;两个置信缺口按缺口写、不当事实;时序指向 #15989。不改代码、不改 schema、不改生成物、无 changeset。为什么改 — 你的原话「15041 应该改为实际 id 保存。选A,其他同意」;Prime Directive #13 要求先改 ADR 再动驱动——ADR-0104 D1 把 DDL 交给了驱动,且 D3 各波次都没排过列收窄,驱动卡 #15989 只能站在一条已记录的决策上动手。
风险与代价(含回滚) — 本 PR 本身零运行时风险(纯文档)。它授权的驱动改动(#15989)才带数据迁移风险:每个带媒体字段的 SQL 部署要经
--apply第 3 步改列;addendum 把「列类型与写编码在一个部署上永不分歧」定为不变量,点名三类部署(尤其 17.0 起出生即打标的库,标志在、列没动),并要求驱动卡按方言、按两种编码打 pin;PG/MySQL 行为与迁移脚本都申报为未实测。回滚:revert 本 PR 即可,#15989 随之保持 blocked。席位意见 — (留空,席位以评论定稿)
你要做的 — 审 addendum 的五点是否与你的裁决一致,尤其两处留给你定的:窗口终止条件定为「驱动落地后的下一个协议大版本 + 未迁移部署启动时响亮拒绝」;Status 行按派发词未动。同意则人工合并;合并后 #15989 解除 blocked。
🤖 Generated with Claude Code
https://claude.ai/code/session_01M59rPZZFzqhfMUPFqqZTkf
Generated by Claude Code