Skip to content

docs(cli): state the measured platform-id width at four citation sites - #16414

Merged
os-litant merged 2 commits into
mainfrom
claude/issue-16114-platform-id-width-citations
Sep 6, 2026
Merged

docs(cli): state the measured platform-id width at four citation sites#16414
os-litant merged 2 commits into
mainfrom
claude/issue-16114-platform-id-width-citations

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes #16114

Four packages/cli sites cited driver-sql's case 'lookup': case 'user': arm by name for the sentence "a platform id is 26 characters", two of them spelling out a ULID literal. #15522 corrected that origin sentence, so the citation chain pointed at text that no longer exists — the arm today carries an explicit guard, "⛔ Do not restore the number this sentence used to carry", which names these four surviving copies and hands them to this card.

This PR makes the four citations true. It changes comment and failure-message text only — no behaviour, no asserted value, no minted width.

The card's premise was re-derived here, not inherited

The dispatch was explicit that the count and the numbers were the card's and unverified by the dispatching seat. Both were re-measured on this branch.

The premise holds. Every claim reproduced.

What the platform actually does — driven, not read

The oracle enters the chain at the top: a real SqlDriver against SQLite, through create() and find(), rather than reading DEFAULT_ID_LENGTH or calling nanoid() directly.

measured result
width of an id the driver MINTS (caller supplies none) 16 — 12 samples, exactly one distinct width. Samples: m3rD4WR6sc6XqF-O, cevYV0RMKDHMMTbk, F6uD2Cz19oQO167i
is that shape a ULID? no — the alphabet carries - and lowercase; a Crockford-base32 check over the samples returns false
a SUPPLIED id, at widths 10 / 17 / 18 / 26 / 40 / 200 all six stored verbatim and read back unaltered
non-vacuity control on that read a query for an id never written returned 0 rows while the table held 6 ⇒ the filter really filters
ULID minted anywhere in the repo noulid case-insensitive over tracked source matches exactly 1 file, and that file is driver-sql's own "do not restore" guard naming the removed literal
positive control on the same instrument nanoid matches 26 files ⇒ the instrument fires, so the 1 is a real reading
is width a platform constant? no, it is driver-owned — driver-sql / driver-mongodb / driver-turso each spell DEFAULT_ID_LENGTH = 16; driver-memory mints objectName-timestamp-counter, a variable-width shape

⚠️ The first attempt at the supplied-id leg was a no-op that read as a pass: the filter shape was wrong, so every read-back returned row 1 and all six comparisons "agreed" at width 10. It is reported rather than quietly swapped out, and the control above is what now makes the leg falsifiable.

The repair therefore does not swap 26 for 16

An id's width is not a fixed number at all. Naming a new fixed number would re-arm the same trap, so each site now says which half it states — what the driver MINTS versus what a supplied id is stored at.

Every site's conclusion is unchanged, which is why this is documentation and not a defect: a reference column takes table.string(...) / VARCHAR(255) rather than table.uuid, because Postgres refuses a platform id in a uuid column with 22P02 — it refuses a 16-character nanoid exactly as it refused a ULID.

The population was swept, and four was a floor

The dispatch asked for the real number rather than the card's. Sweeping 26 char / 26-character variants, the ULID literal, platform id, DEFAULT_ID_LENGTH and id-width phrasings across code, comments, docblocks, tests and docs:

Four in packages/cli — all corrected here (located by text; the triage anchors had rotted, one by 491 lines):

  • packages/cli/src/commands/generate.ts — doc comment, id is 26 characters (...says so and
  • packages/cli/src/commands/generate.ts — inline comment, this card's five rows — a platform id is 26 characters
  • packages/cli/src/commands/generate-field-type-vocabulary.pin.test.tsexpect(value, message) failure string
  • packages/cli/src/commands/generate-multiple-json-column.pin.test.ts — plain comment

A fifth live-source site, outside the card's population — filed as #16410, not edited here: packages/platform-objects/src/audit/sys-import-job.object.ts carries "a minted platform id is 26 characters" — the strongest form, since "minted" is the one reading measurement flatly contradicts. The card listed the platform-objects CHANGELOG copy but not the live source it records. It is filed rather than ridden along on this card's own stated precedent — "deliberately not fixed there — a different package, so a different verification surface" — which measurement confirms: adding that file took the derived gate set from 48 to 49 families, pulling in check:i18n-stale-fill and packages/platform-objects' i18n extractor.

Correctly excluded, verified individually: the two CHANGELOG copies (historical records); driver-sql's arm at sql-driver.ts (it is the correction, and says the opposite); like-pattern.ts, text-match-sql.ts and sql-driver.ts:2761,2767 (the 26 letters of A-Z); validate-rule-schema-formats.ts (format names ≤ 26 chars); notification-keyed-text-bounds.test.ts (uses 255, the physical column width).

packages/rest/src/rest-server.ts is a reserved single-writer path and was not edited. The sweep found no stale citation in it.

Verification

Everything below was run on the final commit, e58829a2563, with the workspace built.

Two commits, and the second corrects the first: f933d1e3c7d made the four corrections, and e58829a2563 reflows one line that commit left orphaned (width half stranded on a line of its own), quoting the three lines it corrects and naming the commit. Nothing is added there and no line is bought — the same words occupy one line fewer.

Suites

  • pnpm --filter @objectstack/cli test272 test files passed (272); 3263 passed, 6 expected fail.
  • pnpm --filter @objectstack/cli typecheck — exit 0. ⭐ Not taken on trust: tsc --noEmit --listFiles was checked to actually contain all three edited files, including the two .test.ts, so "typecheck is clean" is a statement about this diff rather than about a program that excluded it.
  • Targeted re-run on the final commit — the two edited pin tests plus the seven files described below: 9 files, 76 tests, all pass.

⚠️ A first full-suite run reported 7 failed files, and that reading was wrong — reported rather than buried. The dependency closure had been built but not @objectstack/cli itself, and those seven test/ files assert against the package's own dist/. Each failure said so in its own words ("packages/cli is not built... CI declares the build (turbo: @objectstack/cli#test dependsOn build); a direct vitest run does not"). After pnpm build, all seven pass. No diagnosis was assumed: the tests named the cause and the rebuild confirmed it.

Gates — derived, not recalled. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack attests the answer comes from this repo at e58829a2563; the plain form's Reconciliation — 48 famil(ies) and the 48 lines of --commands agree, so the union is reconciled across both spellings.

  • The 48-family union: 47 green, 1 NOT MEASURED. The exception is check:type-check-debt, whose --re-measure half exhausted tsc's heap at the 4 GB ceiling this shared box runs under, and which then refused to record a number rather than report one ("refusing to record 0"; its own text: "⛔ This is NOT a pass and NOT a finding"). Its coverage half printed OK — 75/79 workspace packages type-checked. That is a resource limit, not a verdict on this diff.
  • The Declared WIDE population block — all 10 run, all green. These sit outside the 48 and no derivation can say whether they bite, so their absence from the matched block is not a clearance. They walk every non-test source under packages/, which is exactly what this diff edits, so they were run rather than read as silence.
  • The Artifact roster block — all run, one NOT MEASURED. check:react-declaration-parity needs an SDUI manifest that only a browser build of objectui produces and that this repo's build deliberately does not; unrelated to this diff, which touches no spec or React surface.
  • Two gates are NOT WIRED locally and judge this PR in CI: check:partof-closing-keyword and check:single-claim-paths both refuse without PR_NUMBER/PR_BODY ("a wiring or usage failure, NOT a verdict"). The first is the one that reads this body's closing keyword. This body carries exactly one, on its opening line, and it names this card alone; every other card referenced here is mentioned with no keyword standing before it — checked mechanically over the body and over both commit messages.

The four gates that needed the build were measured, not waved through. check:i18n, check:i18n-coverage, check:i18n-walk-parity and check:dual-build-cjs-loads all reported PREREQUISITE NOT MET on an unbuilt tree — which reads as neither pass nor fail. check:i18n is genuinely implicated here, because it re-extracts translation bundles from packages/cli/src, which this diff edits. After the build all four are green on their own verdict lines, check:i18n reporting OK (9 package(s) — all bundles in sync, no undeclared authoring keys).

No ablation leg, stated rather than invented. The diff changes comment and message text only, so there is nothing whose removal could turn a test red — a mutation here could not fire, and a leg that cannot fire proves nothing.

No mirror pair. Nothing here mirrors a second declaration, so there is no "mutate the side you mirror" leg to run.

No changeset, and skip-changeset applied: this publishes nothing from any released package. Comments and one test failure-message string are the entire diff.


🤖 Generated with Claude Code

https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N


Generated by Claude Code

All four sites cited driver-sql's `case 'lookup': case 'user':` arm BY NAME
for "a platform id is 26 characters", two of them spelling out a ULID
literal. #15522 corrected that origin sentence, so the citation chain pointed
at text that no longer exists -- the arm now carries an explicit "do not
restore the number this sentence used to carry" guard naming the same four
copies.

Measured on this branch by driving the real SqlDriver against SQLite rather
than reading its constants:

  - the driver mints a 16-character nanoid when the caller supplies no id
    (12 samples, exactly one distinct width; the alphabet carries `-` and
    lowercase, so a Crockford-base32 ULID check over them returns false);
  - a SUPPLIED id is stored verbatim at whatever width the caller chose --
    10, 17, 18, 26, 40 and 200 all landed and read back unaltered, with a
    non-vacuity control: a query for an id never written returned 0 rows
    while the table held 6, so the filter really filters;
  - width is driver-owned, not a platform constant: driver-sql, mongodb and
    turso each spell DEFAULT_ID_LENGTH = 16, while driver-memory mints
    objectName-timestamp-counter, a variable-width shape.

So the replacement does not swap 26 for 16. An id's width is not a fixed
number at all, and each site now says which half it states -- what the driver
MINTS versus what a supplied id is stored at -- because naming a new fixed
number would only re-arm the same trap.

Every site's conclusion is unchanged: a reference column takes
`table.string(...)` / VARCHAR(255) rather than `table.uuid`, because Postgres
refuses a platform id in a `uuid` column with 22P02 -- it refuses a
16-character nanoid exactly as it refused a ULID.

Comment and failure-message text only: no behaviour, no asserted value and no
minted width changes. The two CHANGELOG copies stay untouched as historical
records. A fifth live-source site carrying the same sentence in its strongest
"minted" form, in packages/platform-objects, is filed as #16410 rather than
edited here -- a different package, a different verification surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
Commit f933d1e ("docs(cli): state the measured platform-id width at four
citation sites") left the aligned doc-comment block in `generate.ts` reading:

     *                 Postgres refuses either in a `uuid` column with `22P02`. The
     *                 width half
     *                 is the same rule for the whole REFERENCE_VALUE_TYPES class:

"width half" was stranded on a line of its own -- an artifact of where that
commit's replacement text ended, not something the surrounding block ever
said. Rewrapped to close the orphan; the sentence is unchanged word for word.

Presentation only, and no line is being bought: nothing is added here, the
same words occupy one line fewer, and the paragraph's content is identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
@os-litant os-litant added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed tests labels Sep 6, 2026 — with Claude
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/data-flow.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts))
  • content/docs/deployment/cli.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts))
  • content/docs/protocol/kernel/lifecycle.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts))
What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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 — 22 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 fd75728bc65156b9b5e6361d54011bd72c835932packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json fd75728bc65156b9b5e6361d54011bd72c835932

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs fd75728bc65156b9b5e6361d54011bd72c835932 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-litant
os-litant marked this pull request as ready for review September 6, 2026 21:21
@os-litant
os-litant enabled auto-merge September 6, 2026 21:21
@os-litant
os-litant added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 0ea5f9d Sep 6, 2026
42 checks passed
@os-litant
os-litant deleted the claude/issue-16114-platform-id-width-citations branch September 6, 2026 21:44
os-litant pushed a commit that referenced this pull request Sep 6, 2026
…of copying it

`automation-runs-query-validation.test.ts` asserted "forwards every declared
ExecutionStatus member" over a list of eight members typed into the test body.
It was identical to the enum the day it was written and short by one the day
`refused` was appended (#14945) -- so the row stayed green while covering
eight of nine, under a name that promises every member. A pin that copies the
vocabulary it claims to cover stops measuring that claim the moment the
vocabulary moves, and says nothing when it does.

The rows now come from `ExecutionStatus.options`, which is the discipline both
neighbours already apply: the boundary hands `parseEnumParam` that same array
(`domains/automation.ts`), and `automation-api.zod.test.ts` turned its own
inline copy into the same read under #7359. Iterating `.options` only reads it
-- nothing here sorts or re-seats it, because `execution.zod.ts` reserves
those positions for readers that index them ("Appended last so every reader
that indexes `.options` keeps its positions").

Two prose citations of the same set were counting it rather than naming it:

  - `query-param.ts` said `ListRunsRequestSchema` "bounds it to the eight
    `ExecutionStatus` members";
  - `domains/automation.ts` said the schema declares
    "`z.enum([...8 ExecutionStatus members])`".

Both now say what the set is defined BY -- the enum itself, never a copy of
its members -- rather than how many are in it. Deliberately NOT "nine": a
fresh literal re-arms the identical trap and reads authoritative the whole
time, which is the correction #16414 landed hours ago and the reason it
refused to mint a replacement number of its own.

Population, swept rather than assumed: every tracked non-dist file was scanned
for windows carrying four or more of the nine members, and separately for
count words next to `ExecutionStatus`. The matcher's positive control is this
file's own pre-change text, which it reports at eight of nine members with
`refused` missing. Outside the three sites above it finds no live stale copy:
`execution.test.ts`'s eight-member list is the deliberate PREFIX pin for the
appended-last invariant, `AUTOMATION_RESULT_STATUSES` is a different, five
member union held exhaustive by a type-level `Eq`, plugin-approvals'
`TERMINAL_RUN_STATUSES` is a documented narrower subset, and the generated
reference docs carry all nine. One stale copy does survive, in
`docs/qa/platform-checklist/areas/automation.json` -- filed separately rather
than edited here: a JSON checklist has no enum to read, so its repair is an
authoring decision and a different gate family.

Test-only plus comment text. No behaviour, no asserted value and no minted
count changes; the CHANGELOG copies stay untouched as historical records.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
os-litant pushed a commit that referenced this pull request Sep 7, 2026
Round 2 on #15890, after review. Comment prose only — no executable line
moves, the pin and its ablation are untouched and settled.

1. HISTORY. The previous commit on this branch wrote, of
   `ListRunsRequestSchema`, in `domains/automation.ts`:

     "has always declared it — as `ExecutionStatus.optional()`, the enum
     itself rather than a copy of its members"

   and in `query-param.ts`:

     "bounds it to `ExecutionStatus` itself — the enum, never a copy of its
     members"

   Both are FALSE of history, and — the whole lesson — the sentence the
   first one replaced was accurate. `ListRunsRequestSchema` declared
   `status` as an inline
   `z.enum(['pending', 'running', 'paused', 'completed', 'failed',
   'cancelled', 'timed_out', 'retrying']).optional()` from the schema's
   introduction (`5e685a7d6f7`, 2026-02-21) until #7359 replaced that copy
   with `ExecutionStatus.optional()` — landed as `cf7c69421a7`
   (2026-08-11), in the same commit that made this boundary read the
   parameter. So "always" and "never" are both wrong: it was a copy of
   eight members for most of its life.

   Verified by `git show 5e685a7:packages/spec/src/api/automation-api.zod.ts`,
   `git show cf7c694^:` on the same path, and `git log -S` on that
   literal — which names exactly two commits, the introduction and #7359.

   Repairing the stale COUNT had introduced a falsehood about HISTORY, in
   a card whose entire subject is untrue claims about this vocabulary.
   Both sites now say what the line declares today AND when it started
   saying it. No count of the LIVE enum is minted; "eight" is a property
   of a deleted literal, frozen by `cf7c69421a7`, and it is what makes the
   sentence explain itself.

2. `TERMINAL_RUN_STATUSES`. The previous commit called it

     "a documented narrower subset"

   Measured in `plugin-approvals/src/approval-service.ts`, it is
   `{completed, failed, cancelled, timed_out}` — a set its own header
   calls "A CLOSED set, deliberately", whose dead-run sweep treats every
   other answer, an unknown status included, as ALIVE so an unrecognised
   state can never cost someone a live approval. That is a fail-safe
   default, not a narrowing anyone documented relative to `ExecutionStatus`.
   It was closed before `refused` existed, and `refused` — "Terminal,
   never resumed" — is not in it, so once lane 2 (#15788) makes a run
   reach that status, its still-pending approval reads as alive to that
   sweep. A reader of this vocabulary that has not learned the new member,
   owned by the `refused` lanes, not by this card. Corrected in the PR
   body; no tree text carried the wrong wording.

3. "#16414 landed hours ago" (previous commit and PR body). #16414 landed
   `0ea5f9d9f79` at 2026-09-06T21:22:02Z; the commit that said "hours" was
   authored 22:50:48Z the same day — 89 minutes by those two stamps, 66 by
   the review's anchor. Two anchors, two numbers, and the elapsed time was
   never load-bearing: the citation of #16414 stays, the time reference is
   dropped rather than replaced by a third number.

Also corrected in the PR body, not in the tree: "No `dist` is on the
resolved path" was true of the two ablation legs and false of the suite.
Measured on a fresh worktree with nothing built —
`pnpm --filter @objectstack/runtime exec vitest run
src/domains/automation-runs-query-validation.test.ts` cannot collect the
file at all: "Failed to resolve entry for package
@objectstack/observability". `http-dispatcher.ts`, which the suite
imports, imports that package; `packages/runtime/vitest.config.ts` has no
alias entry for it; its `exports` map serves `./dist/index.js`. The two
legs stay source-resolved and `ablation-dist-preflight` still does not
apply to them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants