docs(spec): describe() for SpecialOperator $null and $exists - #15054
Conversation
SpecialOperatorSchema declared both members as bare z.boolean().optional() with a JSDoc comment and no .describe(), so the reference page's Description column -- filled from prop.description, the JSON-Schema projection of a Zod .describe() -- rendered both cells empty. The published page therefore said nothing about the operator pair whose meaning was the subject of a six-site correction campaign. Each member gains a .describe(); the JSDoc stays. Regenerating with check:generated --fix touched exactly two rows of content/docs/references/data/filter.mdx and nothing else. Prose only: no accept/reject or shape change, the JSON-Schema delta is a description string on two properties. Wording written against scripts/check-corpus-claim-drift.mjs rather than into it: its exists-key-presence row reads 4 sites before this diff and 4 after the page was regenerated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174WZTU6XcFcS7g2kykC53i
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not 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
Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6bbc255d1d21fc7ecd4dfbb1cbc5de728e03742d && git checkout 6bbc255d1d21fc7ecd4dfbb1cbc5de728e03742d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 778c59f75dabe2788ea3bc539b98738fd3fe5b91 79007b38f4f09813e3d61a493fd24410064ec944 && git checkout -B drift-repro 778c59f75dabe2788ea3bc539b98738fd3fe5b91 && git merge --no-ff 79007b38f4f09813e3d61a493fd24410064ec944
node scripts/docs-audit/affected-docs.mjs --json 778c59f75dabe2788ea3bc539b98738fd3fe5b91 |
|
Landing provenance (PM seat
Generated by Claude Code |
Fixes #14048
Clause ②: no — path limb fires (
packages/spec/src/data/filter.zod.tsdescribe text); content limb no: no accept-set or shape change.What was wrong
SpecialOperatorSchemadeclared both members as barez.boolean().optional()with a JSDoc comment and no.describe().content/docs/references/data/filter.mdxfills its Description column fromprop.description— the JSON-Schema projection of a Zod.describe(), read atpackages/spec/scripts/lib/schema-section.ts:437— so both cells rendered empty. The published reference page therefore said nothing at all about the operator pair whose meaning was the subject of a six-site correction campaign (#13539, #13709), and$existsis the operator where getting it wrong changes which rows an RLS predicate admits.What changed
Two
.describe()calls on the existing members, and the page regenerated from them. The JSDoc stays; the describe restates it.packages/spec/src/data/filter.zod.ts—$nulland$existsinSpecialOperatorSchemaonly.content/docs/references/data/filter.mdx— regenerated, never hand-edited.pnpm --filter @objectstack/spec check:generated --fixreportedcontent/docs/references/**as the single stale artifact of 15 and regenerated only that one; the resulting diff is exactly rows 138-139 and nothing else..changeset/spec-special-operator-describe.md—@objectstack/specpatch.The mechanism hypothesis was verified rather than assumed:
.optional().describe(...)— describe on the optional wrapper, the shape already used by$in,$nin,$icontains,$like,$ilikein this file — does reachprop.descriptionand does render. That is what the regenerated rows prove.Ratchet: run before the wording was written, and again after regeneration
scripts/check-corpus-claim-drift.mjsruleexists-key-presenceis shrink-only and watchescontent/docs/**, which is where this page lands, so it was run first and the prose was written against it.check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling./Rules: 4 row(s) — exists-key-presence 4, exists-portability 0, regex-retired 0, section-visiblewhen-unbound 0.exists-key-presence 4.The new prose adds zero claim sites. It says the field HAS A VALUE and never touches the key-presence family of phrasings; the
$existscell also stays clear of theexists-portabilityrow (it says portable, and never names a backend restriction).Measured and reported, deliberately not acted on
Triage 5488438905 scoped this card to the one pair and asked for the file-wide number to be measured and reported so the next card has a basis. Both readings are of
packages/spec/src/data/filter.zod.tsat this branch's merge base:.describe(call sites: 11 before this PR, 13 after. The card said 15 and triage counted 14; both are drift, the current number is 11. Command:grep -c '\.describe(' packages/spec/src/data/filter.zod.ts..describe(): 12 before this PR, 10 after. They are$eq,$ne(EqualityOperator),$gt,$gte,$lt,$lte(ComparisonOperator),$contains,$notContains,$startsWith,$endsWith(StringOperator), and the two this PR converts. Of 43 Zod object members in the file, 9 carried a.describe()before this PR (11 after), 12 were JSDoc-only, and 22 carry neither.Not acted on, per the triage fence. One observation for whoever picks up that card:
FieldOperatorsSchemarestates$nulland$existsaround line 1097 with no JSDoc and no describe, so theFieldOperatortable on this same page still renders those two Description cells empty. That is inside the file-wide sweep this card explicitly deferred, so it is left alone here.Verification
Verified at
79007b38(git rev-parse --short HEADafter the final commit; every gate below ran on that tree).pnpm --filter '@objectstack/spec...' build—os-verify-lock: VERDICT command-exit 0,check-dts-emitted: @objectstack/spec - 34/34 declared declaration file(s) present.pnpm --filter @objectstack/spec test—VERDICT command-exit 0;Test Files 463 passed (463),Tests 12374 passed (12374).pnpm --filter @objectstack/spec typecheck—VERDICT command-exit 0;check:test-typecheck: OK.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed; it took the change set from the merge base itself) named 71 families. All 71 were run. 67 green, 4 NOT MEASURED, none red.Named verdict lines from the ones this diff is about:
check:generated—All 15 generated artifacts are up to date.check:api-surface—@objectstack/spec public API surface + factory signatures unchangedcheck:export-origins—export-origins/ is current: 5239 exports across 17 entry points resolve exactly as recorded.(no new export, as expected)check:authorable-surface,check:docs,check:llms-txt,check:skill-refs,check:strictness-ledger,check:variant-docs,check:yaml-examples,check:quick-reference-counts,check:doc-anchors,check:doc-authoring,check:docs-audit-scope,check:docs-redirects,check:docs-single-h1,check:merge-driver,check:published-files,check:changeset-gate-self-tests,check:pm-half-states,check:spec-parsed-alias,check:closing-keyword-parity— all green.check:nul-bytes—OK (scanned 8174 text file(s) -- 8174 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).Plus a hand scan of the three touched files for ASCII control bytes: no matches.The 4 NOT MEASURED, each with the gate's own reason — none is a red, and CI runs all of them:
scripts/check-dev-prereqs.mjs—The workspace is not built — 1 unmet precondition, not a list of problems.Only@objectstack/specplus its dependency closure,formula,lintandclient-reactwere built here; a whole-workspace build is the repo-wide run CI owns.scripts/check-test-completeness.mjs— exit 3, and its own text says so: the gate needs a savedturbo run testlog, and the derived family names it with no argument, sothe local reading for this gate is NOT MEASURED.scripts/pm/check-half-states.mjs(bare invocation) — a live GitHub patrol; it exceeded the 300s per-command cap used here. Its runnable family formpnpm check:pm-half-statesis green (check-half-states self-test: 2062 cases pass.).pnpm check:dual-build-cjs-loads— exit 3,PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/.Same whole-workspace build as above.Three gates were prerequisite-blocked on the first pass and are counted green only after their package was built and they were re-run:
check:doc-formula-expressions(needed@objectstack/formula),check:doc-security-posture(needed@objectstack/lint),check:skill-examples(needed@objectstack/client-react).One reading worth flagging for the reviewer:
dispatch-gates.mjsprinted a STALE TREE warning —mainmoved 7 commits ahead during this run, and 4 files the derivation reads changed in that range (.github/workflows/stale.yml,scripts/pm/check-clause2-carriers.mjs,scripts/pm/check-skill-line-ratchet.mjs,scripts/pm/ensure-pm-labels.sh). The family count was 71 on both derivations, and none of those four is a gate this diff touches. The branch is left at its merge base rather than merged forward.Generated by Claude Code
Generated by Claude Code