Skip to content

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

Draft
claude[bot] wants to merge 2 commits into
mainfrom
claude/issue-15041-adr-0104-addendum-file-column
Draft

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
claude[bot] wants to merge 2 commits into
mainfrom
claude/issue-15041-adr-0104-addendum-file-column

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

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 (the check:adr-symbol-anchors grammar); 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:

Option A. The physical column for file / image / avatar / video / audio holds the actual id — a bare sys_file id string in a string column — not a JSON-quoted id in a JSON column. The driver is the side that moves; the generator's VARCHAR(2048) already states the ruled end-state and stands. ⛔ B (generator copies the driver's JSON column) and C (status quo) are rejected: the maintainer's ground is the stored form itself — the id is the value, and the column should say so.

  1. ADR-0104 addendum first (Prime Directive [WIP] Add Chinese version of the documentation #13; ADR-0104 :162 hands DDL to the driver and schedules no narrowing). Governed surface docs/adr/**: draft PR, review requested from os-zhuang and hotlong, human merge. The addendum declares: the file family's stored column is a string column holding the bare id; the encoding switch is per deployment, keyed on the existing adr-0104-file-references flag (after os migrate files-to-references --apply reports zero blocking rows), never per version; the dual-encoding window that implies, and its end condition. domain:spec seat drives — this card stays domain:spec as the addendum carrier.

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

  1. Column — for every FILE_REFERENCE_TYPES member the single-value physical column is a string column holding the bare sys_file id; the generator's VARCHAR(2048) / table.string is the end-state and the driver moves to it (the family leaves JSON_COLUMN_TYPES; isJsonField / formatInput / formatOutput stop treating it as JSON; varcharColumnChars mirrors the width). multiple: true media 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.
  2. Switch — per deployment, keyed on the existing sys_migration row adr-0104-file-references (FILE_REFERENCES_MIGRATION_ID; written by os migrate files-to-references --apply only when backfill and reconciliation report zero blocking findings, or creation-attested for a datastore born empty; read memoized through isFileReferencesMigrationVerified), 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.
  3. Window — un-flagged deployments keep today's JSON-quoted id in a JSON column; flagged deployments, after step 3, hold the bare id in a string column; the driver reads both encodings on every dialect throughout. Invariant: on one deployment the column's type and the driver's write encoding never disagree (a quoted write into a varchar is driver-sql schema-drift: the json-vs-text type_mismatch finding is keyed to field.multiple only, 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 --apply runs — 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 / initObjects stay additive and never retype an existing column (measured); the generator is unchanged.
  4. Gaps, stated as gaps — Postgres/MySQL behaviour is reasoned from the driver source, not measured on a live cell (what settles it: the driver card's per-dialect pins for both encodings run against the live services of the 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).
  5. Sequencingdriver-sql: store the file family (file / image / avatar / video / audio) as the bare sys_file id in a string column — drop FILE_REFERENCE_TYPES from JSON_COLUMN_TYPES, per-deployment switch on the adr-0104-file-references flag (ruling on #15041, step 2) #15989 (domain:engine, pm:blocked on 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 bare sys_file id in a string column — drop FILE_REFERENCE_TYPES from JSON_COLUMN_TYPES, per-deployment switch on the adr-0104-file-references flag (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

  • The ADR's Status bullet (the record's running index of its addenda) is untouched — the dispatch asked for the existing sections to stay untouched. If the convention wants a one-sentence pointer there, say so; it is one push.
  • The end condition of the window is located on the release axis (first protocol major after the driver lands, with the loud boot refusal) because "every deployment has moved" is not a fact anyone can observe under the ADR's own 2026-07-27 principle. The ruling left the end condition to the addendum; it is one sentence to change if a different vehicle is wanted.

Changeset

None, deliberately: docs/adr is not a package and nothing publishes from this diff, so the Check Changeset job's opt-out applies. The skip-changeset label 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 HEAD after the only commit; git diff HEAD empty; diff vs base f7db8f4fd is this one file, +288/−0; TURBO_SCM_BASE=f7db8f4fd pnpm exec turbo ls --affected → "0 no packages", so no package test / typecheck is owed and pnpm 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 direct grep -naP over 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 3 PREREQUISITE NOT MET (@objectstack/formula / @objectstack/lint not 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 is content/**, 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_file id 的字符串列(生成器的 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

…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
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

CI note from the dispatching seat, for the reviewers — the one red check on 58f98996a is not this PR's. Lint & Repo Gates fails at the step Merge-driver wiring gate (pnpm check:merge-driver), red on main itself since ~17:22Z for every PR in every lane: the check-regen-pending.mjs --self-test fixture runs pnpm -s in an unpinned directory and Corepack resolves pnpm latest = 12.3.4, whose CLI rejects -s. Anchor cards #15990 / #15992 / #15994; the one-file fix is PR #16002, currently in the merge queue with its group's checks passing. This documentation-only PR touches nothing that gate reads; every other check on this head is green (the earlier TypeScript Type Check red on ba6967254 was the superseded run cancelled by the second commit). Once #16002 lands, the branch merges main by tooling so CI re-runs green. Governed docs/adr/**: this PR stays a draft for os-zhuang / hotlong; the seat never marks it ready or merges it.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant