Skip to content

feat(spec): publish the dimensionless marker on the reference page - #18684

Merged
os-bill merged 2 commits into
mainfrom
claude/issue-18500-dimensionless-docs-half
Sep 17, 2026
Merged

os-bill merged 2 commits into
mainfrom
claude/issue-18500-dimensionless-docs-half

Conversation

@os-bill

@os-bill os-bill commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18500
Clause-②: no

PR #18486 landed the dimensionless schema marker with its gate reader and no docs half. Ruling B on #14478 put both halves in the mechanism, verbatim:

declared ON THE SCHEMA, never in a gate ledger … marker the gate honours and the docs generator publishes

The sibling marker externalVocabulary has had its renderer in packages/spec/scripts/lib/schema-section.ts since #15626. dimensionless had none, so a key marked dimensionless would carry an in-schema declaration that the published reference page says nothing about.

The gate's own header already asserts the half that did not exist. check-duration-unit-keys.ts, exemption class 4: "Same mechanism as class 2, same literal-only validation, same visibility." Class 2 is externalVocabulary, and its visibility is that renderer. This PR is what makes that sentence true.

What landed

  • dimensionlessNote(prop) in packages/spec/scripts/lib/schema-section.ts, beside externalVocabularyNote(prop), appended to the same description cell rather than given a column.
  • Six pins in packages/spec/scripts/schema-section.test.ts, mirroring the sibling's block one-for-one.

A key described Failures seen in the last 5 minutes and carrying .meta({ dimensionless: 'failed attempts' }) now publishes:

Failures seen in the last 5 minutes (dimensionless — counts failed attempts)

Why that wording

The card suggested "counts what" and wrote "⛔ no ruling implied", so the shape was read off the declaration instead of taken from the suggestion. The marker's declared value is a non-empty string literal naming what the number countscheck-duration-unit-keys.ts refuses an empty string and a computed value, because an unverifiable claim exempts nothing — so the declared value is printed verbatim, and the same non-empty-string-literal test decides whether anything is printed at all. The word dimensionless is kept in front of it for the one thing the value alone does not state: the exemption's claim is that the number has no unit, which is exactly the ambiguity a reader has when the prose beside a naked number names a time unit. It also keeps one vocabulary across the marker name, the census line the gate prints, and the ruling.

The three questions the dispatch asked

1. Which gate can actually go red on this change?

packages/spec/scripts/schema-section.test.ts, run by pnpm --filter @objectstack/spec test (vitest project local; the file is not listed in vitest.repo-tests.json) — in CI, Test Core. check:docs and check:generated structurally cannot move: the live population is 0 keys marked, so no reference page changes and check:docs is green both before and after. Established by ablation, not assumed — below.

2. Does the marker ride into the published JSON Schema via z.toJSONSchema?Yes.

  • Direct, on the zod this workspace installs (4.4.3): z.number().meta({ dimensionless: 'failed attempts' }) emits "dimensionless": "failed attempts" on the node, unchanged. A fabricated marker name rides identically, so the channel is generic rather than per-key.
  • On the real generated tree, after pnpm --filter @objectstack/spec gen:schema: 18 files under packages/spec/json-schema/ carry "externalVocabulary" (LIT control), 0 carry "dimensionless", 0 carry the fabricated name (DARK controls). packages/spec/json-schema/ has 0 tracked files, so this cannot be read from git — it was built and read.

the docs generator is not the only publisher. json-schema is in packages/spec/package.json files[], so a marked key would also ship inside the npm tarball. Nothing is marked by this PR, so nothing moves today — but the second publisher is a fact the first card to mark a key inherits.

3. What should it print? — answered from the declared value shape, above.

Clause-② and the changeset

Declared no, unchanged — the dispatch reserved that line, and question 2's answer does not move it for this diff: no key is marked, so no published byte changes.

skip-changeset, measured rather than assumed:

  • packages/spec/package.json files[] = dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json. scripts/** is not among them.
  • dimensionlessNote after the build: 0 hits in every one of those paths. Positive control on the same greps, ObjectSchema: api-surface 2, llms.txt 1, README.md 1, src/**/*.zod.ts 14.
  • dist cannot carry it either way: every tsup entry in packages/spec/tsup.config.ts is under src/, so scripts/** is not an entry.
  • check:docs green ⇒ 0 reference pages move.

⚠️ For the seat that marks the first key: at that moment the marker reaches both publishers, and json-schema/ is published — so that card's declaration and changeset are a different question from this one's.

Testing

Union run at d6f2148892 (the final commit; gate logs carry no sha, so this is the tree every number below was taken on).

run result
pnpm --filter @objectstack/spec test (project local) 484 files passed / 1 skipped, 13872 passed / 1 skipped
pnpm --filter @objectstack/spec typecheck (incl. check:scripts-typecheck, check:test-typecheck) exit 0
pnpm --filter @objectstack/spec check:docs · check:variant-docs · check:liveness · check:empty-state · check:strictness-ledger exit 0 each
pnpm lint (eslint . --no-inline-config, whole repo — not narrowed) exit 0
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ... 57 derived, 52 run, 5 NOT MEASURED, 0 UNRUN

The 5 NOT MEASURED are check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content, check:type-check-debt — each exited 3, PREREQUISITE NOT MET, which those gates spell out as "not a pass and not a finding". All five read built output of the whole monorepo, which this worktree does not have; CI's Build Core is where they are measured. This diff adds no export, no entry point and no emitted file, so it has no channel to reach any of them.

Red before green — ablation with an on-disk proof and a hash-verified restore

Run from the committed state, under a trap ... EXIT INT TERM, against absolute paths:

  1. HEAD:packages/spec/scripts/lib/schema-section.ts blob = a92ba895b1f51e8eaf8945e45f8bf8c12ea77e90.
  2. Anchor count before mutation: 1. Mutation deletes the dimensionlessNote(prop) term from the description cell.
  3. Landed on disk, both directions: deleted text 0 occurrences after, injected marker 1 occurrence after; mutated blob 4f00017eab... differs from the HEAD blob.
  4. Red leg: Tests 4 failed | 39 passed (43) — the four cases that assert the note is printed. The two that assert its absence stayed green, which is the predicted direction and what keeps the note from decorating every row in the reference.
  5. Restore leg: git checkout HEAD -- path (never a bare git checkout --), then hash re-read = a92ba895b1..., matching, and git diff HEAD empty. Green re-run at the final head: 43 passed.

Predicted direction was stated before the run and matched: turn red, not "more diagnostics" and not "inverted".

skills/** derivative

None. Reading, not expectation: the diff is exactly 2 files (git diff --name-only against the merge base), 0 of them under skills/. packages/spec/scripts/build-skill-docs.ts does not import schema-section, and check:skill-docs / check:skill-refs were not derived for these paths. So no net-line-count reading is owed.

Acceptance notes

Out of scope, noted and deliberately not filed:

  • The .meta() channel is generic, not a marker allowlist. A fabricated key name rides z.toJSONSchema into json-schema/ exactly as externalVocabulary and dimensionless do, and json-schema/ is published. This is the documented mechanism (xRef / xExpression / xEnumDeprecated use the same channel by design), not a defect, and none of the three filing classes fits: no repro, no contract violated, and the hazard is metadata being published rather than silently dropped or refused. Who will meet this file next: spec: the genuine duration rows adopt a declaration — DurationMs/DurationSeconds or a unit-suffixed name (census #18078 classes A-true ×6, B ×2, D ×≥2) — step ③ of ruling A on #18115 #18124 step 4, the card that will first mark keys.
  • Nothing mechanically ties "a marker the gate reads" to "a renderer in schema-section.ts". The next marker added to check-duration-unit-keys.ts can repeat exactly this half-build. Not filed: it is undemonstrated drift today, ruling B is honoured on the tree as of this PR, and both markers that exist now carry both halves.

Dispatch fences observed

packages/spec/src/migrations/** and scripts/check-cross-package-test-inputs.mjs untouched (the latter moved on main under #18667 and arrived through a plain merge of origin/main, with no conflict and no edit by this branch). packages/spec/scripts/check-duration-unit-keys.ts read only. No live key was marked dimensionless — population stays 0, by measurement above.


Generated by Claude Code

The `dimensionless` schema marker landed with its gate reader and no docs
half. Ruling B on #14478 put both halves in the mechanism: a marker
"declared ON THE SCHEMA, never in a gate ledger … the gate honours and the
docs generator publishes". Its sibling `externalVocabulary` has had its
renderer since #15626; this one had none, so a key marked `dimensionless`
would carry an in-schema declaration the published reference page says
nothing about.

Adds `dimensionlessNote()` beside `externalVocabularyNote()` in
`scripts/lib/schema-section.ts`, appended to the same description cell, and
reading the same declaration the gate reads: a non-empty string literal, so
an empty or non-string marker publishes nothing.

No key is marked by this change — the live population stays 0 — so no
reference page moves and no published payload changes.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@os-bill os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

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 — 0 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 551139bb78785441c2fee72acd87f0ce835f683bpackageMentionDocs.

@os-bill
os-bill marked this pull request as ready for review September 17, 2026 15:02
@os-bill
os-bill enabled auto-merge September 17, 2026 15:02
@os-bill
os-bill added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 17965df Sep 17, 2026
41 checks passed
@os-bill
os-bill deleted the claude/issue-18500-dimensionless-docs-half branch September 17, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

the dimensionless marker lands with a gate reader but no docs-generator half — #14478 ruling B put both in the mechanism

2 participants