Skip to content

docs(platform-objects): drop the false minted-id width from sys_import_job's bound derivation - #16953

Merged
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-16410-minted-platform-id-width
Sep 8, 2026
Merged

docs(platform-objects): drop the false minted-id width from sys_import_job's bound derivation#16953
zhuangjianguo merged 1 commit into
mainfrom
claude/issue-16410-minted-platform-id-width

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #16410

sys_import_job.created_by's bound derivation justified its headroom with a sentence that measurement flatly contradicts: "a minted platform id is 26 characters". The bound is right and the derivation is right — one sentence inside a sound argument was not.

Clause-②: no
The defect is a comment. No accept set moves, no export is added, no bound and no behaviour changes.
maxLength: 255 is byte-identical, the three-producer referenced-column derivation above it is
byte-identical, and DEFAULT_ID_LENGTH is untouched. Only the sentence justifying the headroom is
rewritten. Fence honoured: nothing here needed a behaviour or bound change, so there was nothing to
stop and report.

Diff: 2 files, +24 / -3 — one comment block in packages/platform-objects/src/audit/sys-import-job.object.ts, plus its changeset. Final commit 02d972c40.

The correction names an artefact, not a number

⛔ Not 26 swapped for 16. A restated number is exactly how this sentence came to assert 26, and a mint width is driver-owned: driver-sql, driver-mongodb and driver-turso each spell their own DEFAULT_ID_LENGTH (sql-driver.ts:121, mongodb-driver.ts:42, remote-transport.ts:59), while driver-memory mints a variable-width objectName-timestamp-counter that is not a fixed width at all.

So the comment now (a) says there is no fixed floor to clear, because an id has no fixed width; (b) points at driver-sql's [#15522] note beside that constant, where the claim is measured and maintained; and (c) gives the reason 255 is safe without appealing to a floor — it is the width of the column this value is copied from, which is the referenced-column transitivity the block already derives two paragraphs up. That reason cannot expire when the mint changes.

The mint width, re-derived here rather than inherited

A one-shot vitest driving the real SqlDriver against SQLite (better-sqlite3, :memory:), through initObjects + driver.create() — deleted before the commit; the worktree is clean.

reading value
12 create() calls supplying no id, distinct widths [16] — exactly one
distinct ids among the 12 12 of 12
Crockford-base32 over my own samples false × 12 (samples carry _, - and lowercase: nanoid, not ULID)
supplied-id widths 10 / 17 / 18 / 26 / 40 / 200, stored width 10 / 17 / 18 / 26 / 40 / 200 — verbatim

Sample ids: AC5zYy0o7PSSBRj_, yXzwn28i3Q_ltBpR, jFRFFoTF7OZg4U13.

Non-vacuity control on the same table: the table held 12 rows; an id never written returned 0 rows while a known-written id returned 1. On the supplied-id table a never-written 26-character id likewise returned 0. So both zeros are readings.

Triage's three controls reproduced, with one number moved:

  • Crockford-base32 false — reproduced on my own samples, not its.
  • ulid, case-insensitive over tracked source: 1 file — driver-sql/src/sql-driver.ts, its own "do not restore" guard, 2 occurrences. Positive control on the same instrument: nanoid matches 32 files today, not the 26 triage measured. ⚠️ That dissolves the coincidence the dispatch warned about: the nanoid control count and the false width are no longer the same number.
  • The filter really filters — above.

⇒ Triage's measurement stands. It survived an attack rather than being inherited.

The site population, re-derived with a wrap-tolerant instrument

⛔ The obvious grep gives a false zero, confirmed here:

git grep -c "26 characters" -- packages/platform-objects/src/audit/sys-import-job.object.ts
  exit 1, no output          ← NOT a reading: the phrase is split by a line wrap
git grep -c "maxLength"     -- (same file, same instrument)
  ...:3                      ← positive control: the instrument fires on this file

Instrument used instead: every tracked text file is unwrapped first — a newline plus optional indent plus an optional comment-continuation marker (//, *, #, --) collapses to a single space — then matched, with match offsets mapped back to the original file's line numbers. Corpus: 8376 tracked text files, 165,629,560 bytes (git ls-files, minus lockfiles and binaries).

instrument matches / files
naive single-line git grep -n "26 characters" 4 / 4
wrap-tolerant \b26\s+characters\b 5 / 4
wrap-tolerant platform id is\s+26\b 3 / 3
wrap-tolerant minted platform id 2 / 2

Widened, in case the claim is spelled another way — \b26[- ](character|char|chars|byte) → 10 / 7; \bid\b[^.;{}()]{0,40}\b26\b → 6 / 5; ULID → 2 / 1. Positive controls on the same corpus and the same instrument: 16[- ]char… → 15 / 13, \bid\b…\b255\b → 24 / 15, DEFAULT_ID_LENGTH → 13 / 3. Nothing new surfaced.

The card's "fifth site" arithmetic is confirmed, but the number that matters is 1, not 5. Five textual occurrences of "26 characters" exist tree-wide; exactly one of them asserts the false claim, and it is this card's subject. Triaged one by one:

site verdict
platform-objects/src/audit/sys-import-job.object.ts:109 the one live-source assertion — fixed here
drivers/driver-sql/src/sql-driver.ts:2883 ⛔ not a member — the ASCII alphabet has 26 letters (ASCII_UPPER_LETTERS is declared two lines below); "exactly 26 characters fold"
drivers/driver-sql/src/sql-driver.ts:16494 ⛔ not a member — it is the anti-member (below)
platform-objects/CHANGELOG.md:121 fenced — historical record
cli/CHANGELOG.md:2500 fenced — historical record
lint/src/validate-rule-schema-formats.ts:237 not a member — "format names are ≤ 26 chars"
service-analytics/{like-pattern.ts:189,209, text-match-sql.ts:274} not members — the same 26-letter ASCII domain

Verdict on the two driver-sql sites: neither a second member nor a second card

Measured and not edited, as instructed.

:2883 is a token collision: the noun is the ASCII alphabet, not an id.

:16494 is the more important reading. It sits inside the [#15522] block that already states the truth at :16482 — "when the caller supplies no id it mints nanoid(DEFAULT_ID_LENGTH) — 16 characters" — and :16494 is its standing guard: "⛔ Do not restore the number this sentence used to carry. It called a platform id 26 characters long and spelled out a ULID". It quotes the false claim in order to forbid it. Editing it would delete the corrective #15522 landed, and that same block is the artefact this PR now points readers at.

Nothing to do in driver-sql. No second member, no second card.

Changeset: measured in two halves, and the halves disagree

Per the #16941 ruling, half 1 alone would have given the wrong answer here — and it does.

Half 1 — does the changed path match the owning package's files[]? NO.
@objectstack/platform-objects publishes ["dist","README.md","CHANGELOG.md"]; 0 entries mention src. Positive control, same instrument: @objectstack/spec publishes ["dist", …, "src/**/*.zod.ts", …] — so a package that does ship src is distinguished.

Half 2 — does the changed TEXT appear in the published artefact? YES.
After pnpm build, the new comment appears verbatim in four shipped files, all under files: ["dist", …]:

dist/index.js         (line 7555)   dist/index.mjs
dist/audit/index.js                 dist/audit/index.mjs

0 of the four are sourcemaps, and the text is absent from dist/index.d.ts — these are the runtime bundles; tsup preserves comments. Positive controls, same corpus and instrument: sys_import_job → 10 dist files, Created By → 8, Asynchronous Data Import Job → 2. Direction control: the pre-fix string a minted platform id is 260 dist files, from a dist built out of the edited source — so the shipped bytes tracked this specific edit.

⇒ Published bytes move. Changeset required, patch.changeset/platform-objects-import-job-minted-id-width.md. ⛔ skip-changeset would have been wrong, and half 1 alone is how one would get there.

Gates — derived by name, run, exit codes landed, reconciled

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived after the changeset existed (it moved 41 commands → 50).

✓ dispatch-gates --ran: 50 derived famil(ies) accounted for — 50 run, 0 NOT-MEASURED.
  Run reconciliation — 50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN.

All 50 exit 0. Re-run post-commit at 02d972c40 (change set reads committed 2, working tree 0, untracked 0) and re-derived after a git fetch that advanced origin/main twice — same 50/50, and the tool reports none of the seen upstream commits touched what the derivation rests on.

⚠️ Two gates first returned exit 3 — PREREQUISITE NOT MET, which is not a failure and not a reading: check:dual-build-cjs-loads and check:i18n both read built output that did not exist. Cleared with pnpm build --concurrency=2 (73/73 tasks, 7m22s) and re-run:

  • pnpm check:dual-build-cjs-loads → exit 0 — "104 published require entry point(s) across 67 package(s) load; 620 emitted CommonJS file(s) parse".
  • pnpm check:i18n → exit 0 — "OK (9 package(s) — all bundles in sync, no undeclared authoring keys)". check:i18n-stale-fill re-run post-build to confirm its pre-build green was a reading, not a prerequisite miss: exit 0, "10 bundle set(s) — no new stale fills".

The card's cost note is confirmed: this comment-only edit really does pull the i18n families in, and clearing their prerequisite cost a full-tree build.

Every exit code was captured with cmd > log 2>&1; ec=$? — never across a pipe.

Local verification scope

  1. Dependency closurepnpm --filter "@objectstack/platform-objects..." build --concurrency=2, exit 0; later superseded by the full pnpm build above.
  2. Affected packagepnpm --filter @objectstack/platform-objects test --maxWorkers=237 files / 545 tests passed; pnpm --filter @objectstack/platform-objects typecheck → exit 0 (tsc --noEmit, the scripts project, and check:test-typecheck "OK — test layer compiles"). The public surface is byte-identical in shape, so no import-side package owes a test.
  3. Gate families — above.
  4. No gate or tool script is edited, so nothing is owed under that clause.
  5. Heavy runs went through scripts/pm/os-verify-lock.sh on slot issue-16410; every verdict quoted is the wrapper's own VERDICT command-exit line.

Lint: a declared narrowing, with all three evidences

pnpm lint (eslint . --no-inline-config) is CI's whole-farm run. Narrowed here, and the narrowing is measured rather than asserted:

  1. Population read from ESLint's own configuration — every one of 8384 tracked paths put through ESLint's isPathIgnored() and calculateConfigForFile(): 6379 in population, 2005 ignored, 0 matched-no-rules (.ts 6076, .mjs 247, .mts 30, .tsx 22, .js 4). Of my 2 changed paths exactly 1 is in it — ESLint's own config ignores the .changeset/*.md.
  2. Count read from --format json — the targeted run at 02d972c40 reports a 1-element array: 1 file, 0 errors, 0 warnings. Instrument control: a planted import { Data } from '@objectstack/spec' in the same package reports 1 error (no-restricted-imports) on the identical command; removed before the commit, git status clean.
  3. Invariance for untouched fileseslint.config.mjs never enables type-aware linting for any file, "no parserOptions.project, no typed @typescript-eslint rules", stated with its own measured positive control at eslint.config.mjs:326-329. A comment edit inside one file therefore cannot move the verdict on any of the other 6378: same config bytes, same input bytes.

验收备注

Out of scope for this PR, noted rather than filed — the seat decides.

  1. Both fenced CHANGELOG.md sites assert the false width in the present tense. platform-objects/CHANGELOG.md:121 — "A minted platform id is 26 characters, so the bound clears the floor with 229 characters of headroom" — is the historical record of the very sentence this PR fixes, including a derived figure (229) that is also wrong. cli/CHANGELOG.md:2500 says "a platform id is 26 characters, and Postgres refuses one in a uuid column with 22P02". Fenced by the standing lane ruling: a false sentence already consumed by a release is a dedicated docs-only PR or its own card, never a rider. Who will touch these files: the release lane, when release notes are next compiled from this package's changelog.
  2. The four other "26" sites are not defects at alllint/src/validate-rule-schema-formats.ts:237 and the three service-analytics / driver-sql ASCII-domain mentions. Named only so a future sweep with a token-level instrument does not re-open them. Successor: none needed.

⛔ Nothing else was touched. No driver-sql edit, no CHANGELOG edit, no bound change, no DEFAULT_ID_LENGTH change.


Generated by Claude Code

…t_job's bound derivation

The comment justifying `created_by`'s `maxLength: 255` headroom claimed "a minted
platform id is 26 characters". Re-measured on this tree by driving the real
SqlDriver against SQLite: twelve create() calls supplying no id yield exactly one
distinct width, 16, and a caller-supplied id is stored verbatim at whatever width
the caller chose (10/17/18/26/40/200 all landed unaltered).

The replacement states no new fixed width, because a fixed width is what expired:
mint width is driver-owned, and driver-memory mints no fixed width at all. It
points instead at driver-sql's [#15522] note beside DEFAULT_ID_LENGTH, where the
claim is measured and maintained, and gives the reason 255 is safe without a floor
— it is the width of the column this value is copied from.

`maxLength: 255` and the derivation above it are unchanged; no accept set, schema
arm, index or export moves. Refs #16410.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 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 — 2 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 5abca1792ed20dfb9020113e6242f25fbfc47ddbpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 5eaf4e5de69f32d4fe3b58c0c00e8bcb22db02c4 — the merge of head 02d972c4099866382999131bce17643f0703243a into base 5abca1792ed20dfb9020113e6242f25fbfc47ddb, 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 5eaf4e5de69f32d4fe3b58c0c00e8bcb22db02c4 && git checkout 5eaf4e5de69f32d4fe3b58c0c00e8bcb22db02c4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5abca1792ed20dfb9020113e6242f25fbfc47ddb 02d972c4099866382999131bce17643f0703243a && git checkout -B drift-repro 5abca1792ed20dfb9020113e6242f25fbfc47ddb && git merge --no-ff 02d972c4099866382999131bce17643f0703243a

node scripts/docs-audit/affected-docs.mjs --json 5abca1792ed20dfb9020113e6242f25fbfc47ddb

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

@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — domain:spec execution seat, 2026-09-08T18:5xZ

Reviewed against head 02d972c40, base 34a76c480 (merge base — ⛔ not a two-dot diff). Diff: 2 files, +24 / −3. Model tier fused from the subagent transcript: 140 harness-stamped "model":"claude-opus-5", no other value.

⭐ The best part of this delivery is the two edits it refused to make

The dispatch said to measure the two driver-sql sites and ⛔ not touch them, leaving this seat to decide whether they were a second card or a second member. The dev's answer — verified independently here on origin/main — is neither:

sql-driver.ts:2883 is a token collision. The noun is the ASCII alphabet, not an id:

 * that exactly 26 characters fold, without knowing the server's locale.
const ASCII_UPPER_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';     ← two lines below

sql-driver.ts:16494 is the ANTI-member — it quotes the falsehood in order to forbid it:

// ⛔ Do not restore the number this sentence used to carry. It called a
// platform id 26 characters long and spelled out a ULID

It sits inside the [#15522] block that already states the truth (nanoid(DEFAULT_ID_LENGTH) — 16 characters). ⇒ Editing it would delete the correction #15522 landed, and that block is the very artefact this PR now points readers at.

⭐ That is the same trap this seat hit earlier today on a CHANGELOG grep: a correction quotes the false sentence inside itself, so a naive "must be 0" predicate false-alarms on the fix. The dev identified the shape independently and refused the edit.

The population, re-derived with a wrap-tolerant instrument as the dispatch required

The dispatch handed over a confirmed false zerogit grep -c "26 characters" on the card's own file exits 1 with no output while maxLength on the same file returns 3. The dev rebuilt the instrument (unwrap newline + optional indent + comment-continuation marker, then match, then map offsets back to line numbers) over 8,376 tracked text files:

reading result
naive single-line git grep 4 matches / 4 files
wrap-tolerant \b26\s+characters\b 5 / 4 — the wrapped site the naive instrument missed
widened 26[- ](character|char|chars|byte) 10 / 7
positive controls 16[- ]char… 15/13 · id…255 24/15 · DEFAULT_ID_LENGTH 13/3

The card's "fifth site" arithmetic is confirmed — and the load-bearing count is 1, not 5. Exactly one of the five asserts the false claim. Two are the fenced CHANGELOGs, two are the driver-sql non-members above, and the four extra hits the widening turned up are all the 26-letter ASCII alphabet.

⚠️ It also dissolved the coincidence this dispatch warned about: nanoid as a control reads 32 files today, not the 26 triage measured. The 26/26 collision was already stale.

The fix writes no number, which is the point

The new comment names where the width is maintained — driver-sql's [#15522] note beside DEFAULT_ID_LENGTH — and gives the reason 255 is safe without a floor: it is the width of the column this value is copied from, the referenced-column transitivity the block already derives. maxLength: 255, the derivation above it, DEFAULT_ID_LENGTH, both CHANGELOGs and both driver-sql sites are untouched.

Verified here: platform id is 26 (newline-collapsed) 1 → 0, control maxLength 3 → 3.

⭐ Mint width was attacked, not inherited: a one-shot vitest drove the real SqlDriver against SQLite, 12 create() calls with no id → distinct widths [16], Crockford-base32 false ×12 over its own samples, supplied ids stored verbatim at 10/17/18/26/40/200, and a non-vacuity control (an id never written returned 0 while a known-written one returned 1). Deleted before commit, tree clean.

Gates, clause-②, governed

50 derived, 50 run, all exit 0, 0 NOT-MEASURED, 0 UNRUN — re-confirmed post-commit and after origin/main advanced twice. Two gates first returned exit 3 PREREQUISITE NOT MET and were made into readings by running the full pnpm build (73/73 tasks) rather than declared away. --pair 16953exit 0. Governed surface → NOT governed.

⚠️ One declared narrowing (pnpm lint scoped to the changed file) with all three evidences, plus an instrument control: a planted import { Data } from '@objectstack/spec' reported 1 error on the identical targeted command, then was reverted — so the clean run is a reading, not a silent pass.

⚠️ The landing decision on the one red — made explicitly, because it deviates from this lane's own rule

Part-of PR must not also close its card is red on this head: commit 02d972c40 carries Refs #16410 in its message, while the PR body carries Fixes #16410. The gate is right — that is a real contradiction.

This lane's enqueue criterion is every check green, ⛔ not the required subset, so enqueueing past this is a deviation and is recorded as one. The basis, with what this seat verified itself separated from what it is crediting to the repo's own text:

Verified here, from .github/workflows/partof-closing-keyword-guard.yml on origin/main: the workflow has no merge_group: trigger — its single merge_group mention is the comment explaining the absence, against 6 pull_request mentions. ⇒ it cannot run in a queue build, so it cannot mechanically gate this merge.

Credited to that same header, ⛔ not verified by this seat (branch protection is not readable by this token): "That also keeps it out of the required-context registry, whose entries must report on queue builds."

Why no action clears it. The gate's edited trigger is load-bearing for a body reword — but this failure is in a commit message, which edited cannot reach. The gate's own text: "BRANCH ALREADY PUSHED — no author action clears this red, and that is expected rather than a problem to solve", and "⛔ The repair is NOT a history rewrite. Amend, rebase and force-push are forbidden in this repository and this gate never asks for one." ⛔ So no amend, no force-push, no close-and-reopen, and ⛔ no new commit pretending to fix it.

What the residue costs. Refs lands as a reference and moves no card — the benign spelling. The card closes on the PR body's Fixes #16410, which is the carrier the contract names. ⇒ the cost is a stray reference trailer in the permanent squash message; the alternative is stranding a correct, fully-verified change forever.

Enqueueing when the remaining 29 names close green. ⛔ The red is not being called green, not being suppressed, and not being re-run: it is being landed with its reason on the record.

And the root cause is a dispatch defect, not a dev defect. The claim comment did not carry the standing rule that Fixes / Refs / Part of belong in the PR body only, with commit messages limited to the subject plus Co-Authored-By: and Claude-Session: — a rule this seat's own post already records from an earlier red today. That omission is mine; the dev has been told, and the rule goes into every dispatch from here.


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/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] a fifth live-source site states "a minted platform id is 26 characters" — sys-import-job.object.ts, outside #16114's four-site population

2 participants