Skip to content

fix(objects): stop declaring maxLength on the platform's own id column - #16936

Merged
os-musk merged 1 commit into
mainfrom
claude/issue-15335-system-object-id-maxlength
Sep 8, 2026
Merged

fix(objects): stop declaring maxLength on the platform's own id column#16936
os-musk merged 1 commit into
mainfrom
claude/issue-15335-system-object-id-maxlength

Conversation

@os-musk

@os-musk os-musk commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15335

Clause-②: no

sys_metadata_commit and sys_http_delivery stop declaring maxLength: 64 on id. Two deletions, one judgement — the shape the clean siblings already carry.

What was wrong

initObjects emits id, created_at and updated_at itself and skips any declared field colliding with one, so the storage half of such a declaration is discarded before it reaches DDL. #12015 made that discard loud instead of silent, and #12131 then cleared 45 system objects that were declaring id as text. These two survived that sweep because their residue was an attribute rather than a type: nothing can honour a maxLength: 64 on a column the platform emits as varchar(255).

The diagnostic is untouched, on purpose. It is working as designed — it is what #12015 was filed for, because a declared id used to be discarded in silence. Quieting, suppressing or narrowing it was never the remedy; the platform's own declarations getting clean is.

Measured, not inherited

The card's boot numbers are the filer's, taken on a pty from a pristine home. This branch did not inherit them; every number below was taken here. Tree: origin/main at 70f7d6d735, in a dedicated worktree.

Baseline — the whole tree, not just the two named sites. A throwaway harness enumerated every *.object.ts declaration in the repo with the TypeScript parser and ran the driver's own predicate, undeliveredStorageAttributes from packages/drivers/driver-sql/src/builtin-column-collision.ts, over every field declared on a platform-emitted builtin column:

before after
*.object.ts files scanned 112 112
object declarations scanned 117 117
fields declared on id / created_at / updated_at 215 215
declarations that trip the diagnostic 2 0

Both hits were the two the card names, both maxLength: declared 64, column provides 255. The 215 stays put across the two runs, so the zero is a measurement rather than an empty loop.

Boot path. The two shipped declarations, read from each package's built dist/, fed through a real SqlDriver on better-sqlite3 — one create pass, then one alter pass over the now-existing tables:

[sql-driver] collision warning blocks before after
create pass 2 0
alter pass 2 0
total 4 0

⚠️ 4, not 5. This is a two-pass driver-level boot, not the filer's os start on a pty; the block count is a function of how many sync passes a real boot makes over each table, and the declaration set is what this PR changes. The count of declarations — 2 to 0 — is the invariant, and it is the whole population.

⭐ The 17.2.0 comparison table, carried forward

Triage asked that this not disappear along with the card, because it is what shows the warnings are residue rather than a steady state. The filer's measurement, on a pty from a pristine home with an exact-pinned 17.2.0 scaffold as the control:

[sql-driver] warning lines 17.2.0 17.3.0
os start 338 5
pnpm dev 356 5

338 to 5 inside one minor. This PR is about the remainder, and the card was filed precisely because a count that small is the one most likely to be left as permanent background noise. ⚠️ Those two rows are the filer's numbers and are reproduced here as such — neither triage nor this branch re-ran them.

Reverse verification

Run from the committed fix, both legs symmetric, each proved on disk before its verdict was read.

⚠️ Attempt 1 is reported as void, not quietly re-run. It mutated the sources back to maxLength: 64 and its ablation-dist-preflight said "marker present" — but maxLength: 64 occurs 3 times in metadata-core's dist/index.js and 8 times in service-messaging's with no ablation at all (other fields legitimately carry that bound). That pre-flight could not have failed, so it certified nothing.

Attempt 2 mutates to maxLength: 61, a value that occurs nowhere in either source or either dist (verified: 0 hits in all six files first). It trips the diagnostic exactly as 64 did — the trigger is "declared bound is not the column's 255", not the number.

  • Mutation leg — bytes proved on disk by blob hash against the HEAD blob, both files differing; rebuild green; ablation-dist-preflight exit 0 for both packages, marker present in 2 built files each. Then: sweep 0 to 2, boot 0 to 4, and both pins RED by namesys_metadata_commit.id declares maxLength: 61 and sys_http_delivery.id declares maxLength: 61.
  • Restore leggit checkout HEAD -- path (never bare git checkout --), proved by state and not by exit code: whole-tree git status --porcelain is 0 lines, git diff HEAD empty, and both blob hashes byte-equal to their HEAD blobs. Rebuild, then ablation-dist-preflight --absent exit 0 for both — marker absent from all 12 and all 6 built files, "working tree clean against HEAD". Sweep back to 0, boot back to 0, both pins green.

Every mutating script carried trap restore on EXIT INT TERM with absolute paths seeded from git rev-parse --show-toplevel.

Tests

Each package gains an in-package pin, src/objects/builtin-column-storage-attributes.test.ts, holding every object schema it ships to the clean shape — 6 in metadata-core, 7 in service-messaging — with a positive control in the same run so a green is a measurement. Neither pin imports the driver: the authoritative storage/presentation classification lives in one table, FIELD_KEY_STORAGE_CLASS, pinned there against FieldSchema.shape, and a hand-copied second list is how the two halves drift.

Changeset

patch for both packages, measured rather than assumed: @objectstack/metadata-core and @objectstack/service-messaging are both private: false at 17.3.0, and the removal reaches their built dist/ (verified — the key is gone from the emitted declaration, with no default put back in its place). A diff that publishes from a released package takes a patch changeset and ⛔ never skip-changesetAGENTS.md:1036 on origin/main at 70f7d6d735, and still :1036 on this branch, which changes no line of that file — but :1019 in the shared checkout at 9407e18651. Measured on each tree rather than carried over; the dispatch's :1029 belongs to a branch that edits AGENTS.md, and this one does not.

Not breaking: nothing an author can write changes, no export moves, and the attribute was already being discarded before it reached DDL — the physical columns, the accept set and every write path are what they were.

验收备注

  • Cross-lane, by triage's standing concession. sys_metadata_commit is engine lane and sys_http_delivery is services lane; triage assigned both to engine because it is one judgement and two deletions. That concession stands: if the services seat wants its own half, it says so on the card and a sibling card is opened.
  • Out-of-scope observations noted, none filed: sys_metadata_commit.commit_id-class fields elsewhere in these packages keep maxLength: 64 and are correct to — they are ordinary columns the driver actually emits from the declaration, not platform-emitted builtins. The pins above cover only the three builtin names.
  • No repo-wide gate exists for "a shipped declaration trips the builtin-column diagnostic"; the two in-package pins are per-package. A single gate would be a new validation surface and is out of scope here.

Generated by Claude Code

`sys_metadata_commit` and `sys_http_delivery` were the last two shipped
declarations that made a clean boot warn about the platform's own tables.

`initObjects` emits `id`, `created_at` and `updated_at` itself and skips any
declared field colliding with one, so the storage half of such a declaration is
discarded before it reaches DDL. #12015 made that discard loud instead of
silent; #12131 then cleared 45 system objects declaring `id` as `text`. These
two survived that sweep because their residue was an attribute (`maxLength: 64`)
rather than a type — nothing can honour a bound on a column the platform emits
as varchar(255).

Measured on origin/main at 70f7d6d over all 112 `*.object.ts` files (117
object declarations, 215 fields declared on a platform-emitted builtin column):
exactly 2 declarations still tripped the diagnostic, and both are these. Fed
through the real SqlDriver DDL path — one create pass, then one alter pass over
the now-existing tables — they produced 4 `[sql-driver]` collision warning
blocks; after this change the same two runs produce 0, with the 215-field sweep
unchanged so the empty result is a measurement rather than an empty loop.

The diagnostic itself is untouched, on purpose: it is working as designed and is
what #12015 was filed for. Each package gains an in-package pin holding every
object schema it ships to that shape, with a positive control in the same run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
@github-actions github-actions Bot added the size/m label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 2 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 9a89a0040de98fc77046c5b405db1a3935fd7b18packageMentionDocs.

Which tree this was computed on

This run read content/docs from b4ae0f52b325489d65f5c3c8fcc7f0f69ca2517e — the merge of head 54828f4759c54044b67a5b5366ef1fae6874241a into base 9a89a0040de98fc77046c5b405db1a3935fd7b18, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b4ae0f52b325489d65f5c3c8fcc7f0f69ca2517e && git checkout b4ae0f52b325489d65f5c3c8fcc7f0f69ca2517e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9a89a0040de98fc77046c5b405db1a3935fd7b18 54828f4759c54044b67a5b5366ef1fae6874241a && git checkout -B drift-repro 9a89a0040de98fc77046c5b405db1a3935fd7b18 && git merge --no-ff 54828f4759c54044b67a5b5366ef1fae6874241a

node scripts/docs-audit/affected-docs.mjs --json 9a89a0040de98fc77046c5b405db1a3935fd7b18

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Sep 8, 2026
@os-musk
os-musk marked this pull request as ready for review September 8, 2026 20:40
@os-musk
os-musk enabled auto-merge September 8, 2026 20:40
@os-musk
os-musk added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 4df2a98 Sep 8, 2026
35 checks passed
@os-musk
os-musk deleted the claude/issue-15335-system-object-id-maxlength branch September 8, 2026 21:07
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 tests tooling

Projects

None yet

2 participants