Skip to content

Three surveyed VOCABULARY rows for the teaching-corpus claim ratchet: the (NoSQL) portability gloss, the retired $regex spelling, and #13532's section visibleWhen claims - #14604

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-13745-corpus-claim-ratchet-rows
Sep 2, 2026
Merged

Three surveyed VOCABULARY rows for the teaching-corpus claim ratchet: the (NoSQL) portability gloss, the retired $regex spelling, and #13532's section visibleWhen claims#14604
baozhoutao merged 3 commits into
mainfrom
claude/issue-13745-corpus-claim-ratchet-rows

Conversation

@claude

@claude claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #13745

Three new VOCABULARY rows for scripts/check-corpus-claim-drift.mjs, the shrink-only lexical
anti-drift ratchet #13582 shipped with exactly one row. Each candidate was surveyed separately
and none was batched, per the triage ruling: "⛔ 不要把三个候选行打包成一次「加三个词」".

All three ship. Zero baseline entries, zero corpus prose edited, scripts/corpus-claim-drift-baseline.json byte-identical to main.

 scripts/check-corpus-claim-drift.mjs | 410 +++++++++++++++++++++++++++++++++--
 1 file changed, 389 insertions(+), 21 deletions(-)

The whole diff is one file. ⛔ ROOTS unchanged (content/docs, skills). ⛔ packages/spec
untouched — filter.zod.ts was #13709's, and it is already repaired (git grep 'primarily for NoSQL' -- packages/spec returns nothing on this base; PR #14050 merged). ⛔ No .github/**, no
sibling ratchet, no content/docs/releases/**.


Candidate 1 — exists-portability: the (NoSQL) gloss

Measurement handed over on #13582 / PR #13742 and ⛔ not re-derived. Re-confirmed on this base
because the card said verify, do not assume about the baseline cost.

Survey

git grep -n -i -E 'NoSQL' -- content/docs skills (generated references/ excluded, as the gate
excludes it) → 8 hit-lines over 7 files. (The #13588 handover recorded 9/7 at cc837dbf; the
corpus has moved since — the scan is 224 files today, 190 + 34, against 225 = 189 + 36 then.)

site classification
getting-started/glossary.mdx:132 "a Collection in NoSQL" legitimate · no $exists in file
getting-started/glossary.mdx:140 "SQL/NoSQL queries" legitimate · no $exists in file
kernel/contracts/index.mdx:20 "Swap SQL for NoSQL" legitimate · no $exists in file
kernel/contracts/index.mdx:35 "adapter (SQL, NoSQL, API)" legitimate · no $exists in file
protocol/diagram.mdx:173 "SQL / NoSQL Operation" legitimate · no $exists in file
protocol/objectql/index.mdx:8 "across SQL, NoSQL, Graph" legitimate · no $exists in file
protocol/objectql/query-syntax.mdx:526 "it is portable, not a NoSQL-only operator" legitimate, and INSIDE the window$exists on lines 524, 525, 528
skills/objectstack-query/SKILL.md:154 | Operator | Purpose | SQL / NoSQL | legitimate, and INSIDE the window$exists on line 157, three lines away

Live false claims: 0. Baseline entries: 0. Two legitimate hits are inside the window, so
they cannot be kept green structurally — they are kept green by the claim, which is why a bare
/NoSQL/ member (the shape #13582's dev declined to ship) is unshippable: it reddens the repaired
sentence that exists to state the truth.

The row as shipped

claims: [
  /\((?:primarily\s+for\s+|only\s+(?:for|on)\s+|for\s+)?nosql(?:[-\s]only)?\)/,
  /(?<!\bnot\s)(?<!\bnot\s(?:a|an)\s)(?<!\bnever\s)(?<!\bnever\s(?:a|an)\s)\b(?:nosql|mongo(?:db)?)[-\s]only\b/,
],
window: 4,

The claim is the restriction — a parenthesised gloss, or an unnegated "NoSQL-only" /
"MongoDB-only" — never the word. A new row rather than a member on exists-key-presence because it
is a different claim (portability, not presence): its own truth, its own ledger key, its own
separable debt.

Fixtures

Pre-repair (verbatim, the same 75b3bdc86^ / e51c78f0c^ text the existing WAS_* constants
carry): the table row, the // comment inside the os:check block, the shipped skills row and the
filters axis row are all portability sites — and each is asserted to be one portability site
and one key-presence site, i.e. two rows, two ledger keys. The packages/spec twin's longer
gloss ((primarily for NoSQL)) is pinned too, so the row is known to reach the wording #13709
repaired even though that file is outside ROOTS. WAS_HTTP is asserted not to be a
portability site — the two rows do not report the same thing.

Green legs: the repaired portability sentence, two other negation spellings, and six legitimate
NoSQL usages, the skills column header quoted with the two rows under it so $exists really is
three lines away (a one-line fixture would have been green for the wrong reason). Discrimination:
that same header block goes RED the instant | Purpose | becomes | Purpose (NoSQL) |.


Candidate 2 — regex-retired: the retired $regex spelling

⛔ Unmeasured before this card. Survey is this card's work.

What the retirement actually was, established first

packages/spec/src/data/filter.zod.ts:2390 and
packages/spec/src/data/filter-operator-vocabulary.test.ts:114 (read-only): $regex and $options
are the two members of RETIRED_FILTER_OPERATORS; $regex is absent from FILTER_OPERATORS and
was never declared. Retired under #4706 / #5701 / #5702; refused everywhere with
code: INVALID_FILTER / status: 400 naming the replacement — $icontains case-insensitive,
$contains case-sensitive, $startsWith / $endsWith anchored. The row's truth says that, and
only that.

Survey

git grep -n -F '$regex' -- content/docs skills (references/ excluded) → 8 hit-lines over 3
files
, and every one is a retirement notice:

site classification
deployment/troubleshooting.mdx:140 | $regex | $icontains | under ❌ Wrong / ✅ Correct legitimate — teaches the retirement
protocol/objectql/query-syntax.mdx:352 ### $regex — removed legitimate
protocol/objectql/query-syntax.mdx:354 "was never a declared operator and is retired" legitimate
protocol/objectql/query-syntax.mdx:365-368 the four rewrite-table rows legitimate
releases/v17.mdx:3110 historical note on $options: 'i' legitimate — and ⛔ unrepairable anyway: content/docs/releases/** is off-limits in a code PR

Live false claims: 0. Baseline entries: 0. This row is a regression pin: there is nothing
to repair, and the difficulty is entirely that the pages teaching the retirement say the word on
every line.

The row as shipped

claims: [
  /\b(?:supported|valid|available|accepted|allowed)\s+(?:filter\s+|field\s+|query\s+)?operators?\b/,
  /\bfield\s+operators?\b/,
  /\b(?:use|write|pass|supply)\s+[`'"]?\$regex\b/,
  /\bsupports?\s+[`'"]?\$regex\b/,
  /\$regex[`'"]?\s+(?:is|are|remains?)\s+(?:still\s+)?(?:an?\s+)?(?:supported|available|valid|accepted|declared|implemented)\b/,
],
window: 4,

declared is deliberately NOT in the operator-list member, and the survey is what removed it:
query-syntax.mdx:371 says "narrow the query with the declared operators" three lines under a
$regex rewrite row, and :354 says "was never a declared operator" on the same line as the
spelling. A member carrying declared reddens the page that teaches the retirement. It survives
only in the copula form ($regex is declared), which is a claim and not a mention.

Fixtures

Red: $regex inside a **Supported operators:** list, inside a **Field Operators:** list, "Use
$regex for pattern matching"
, "supports $regex on every backend", "$regex is still
supported on MongoDB"
. Green: the retirement page's rewrite table quoted at its real spacing
including the "declared operators" sentence, the ### $regex — removed heading, the "never a
declared operator" line, and the troubleshooting ❌/✅ table. Discrimination: that same rewrite-table
block goes RED when the one word declared becomes supported.


Candidate 3 — section-visiblewhen-unbound: #13532's claims

⛔ Unmeasured before this card.

Survey

git grep -n -F 'visibleWhen' -- content/docs skills (references/ excluded) → 88 hit-lines over
21 files
— an ordinary, heavily used authoring key. The false claim #13532 retired is narrow: a
form-view section visibleWhen does not bind current_user
. The true statement is that it
does (objectui#6110 + objectui#6111), with two qualifications the repair kept — the binding is
client-side only, and the scope belongs to the host, so it is empty on the public
/f/:slug route.

⇒ The claim ⛔ cannot be "unbound near visibleWhen", because two live sentences say exactly that
and are true: the /f/:slug caveat, and the object-level field rule (Field.*({ visibleWhen }),
ADR-0036) which genuinely does not bind current_user and says so two lines from a visibleWhen.
The claim is pinned on the section surface instead.

Measured over the pre-repair tree (b2dea862c^, the state #13532 corrected) and over today's
corpus, at six widths:

window pre-repair sites today's corpus
4 3 0
6 3 0
9 4 0
12 4 0
20 4 0
40 4 0

Live false claims: 0. Baseline entries: 0, at every width. Window 9 is the smallest that
reaches the binding-root table row — it sits nine lines below the nearest visibleWhen
(layout-dsl.mdx:874 vs :865), so at width 4 the clearest false statement on the page is
invisible — and 9…40 yield an identical ledger, so 9 sits at the bottom of a wide plateau at zero
cost. The number is a decision with evidence, exactly as row 1's 4 was.

Honest limit, stated rather than papered over

⚠️ Four of the five pre-repair false sites are caught; the fifth is not. That one
(layout-dsl.mdx:882-884, "an authored section predicate is read by that second renderer alone,
because the object-view chain drops the key before any evaluator sees it"
) spreads its falsity
across three wrapped lines, and claims are matched per line. The only per-line member that would
catch it is the literal drops the key, which is a one-off phrase ban rather than a claim. Left out
deliberately.

Fixtures

Red, read verbatim out of git at b2dea862c^ at the real line spacing: the os:check comment
("on a FormSection, current_user is not"), the binding-root table row (asserted a site at window
9 and not at window 8 — the boundary, on the row's own number), the "two limits" prose, and the
views.mdx field-table row. Green: all four repaired texts, plus ⭐ the object-level field rule that
legitimately does not bind current_user, ⭐ the true "unbound" caveat about /f/:slug, and an
ordinary authored predicate of the shape the other 88 hits have.


The one-row cap moved to an EXACT ENUMERATION

VOCABULARY.length === 1 && VOCABULARY[0].id === 'exists-key-presence' becomes

VOCABULARY.map((r) => r.id).join(',') === SHIPPED_ROW_IDS

with SHIPPED_ROW_IDS naming the four ids in order. ⛔ Never >= 1, ⛔ never "at most N": a
floor lets a row be smuggled in with no survey, which is the one thing the assertion is for. The
assertion keeps its teaching text (why a word is a card, not an edit) and now names each shipped
row, its card and the survey it paid. A second leg proves the comparison discriminates — a
smuggled row fails it — so the assertion cannot pass on a predicate that approves everything.

Ablations — four legs, all red

Implementation committed first. Each leg mutated the committed file, proved on disk that the
anchor count moved and the marker appeared (never an exit code), ran, restored via
git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and re-proved byte identity
against the HEAD blob. No build/dist is involved — the gate runs from source — so no rebuild leg
applies.

leg mutation result
E smuggled row append a fifth VOCABULARY row with no survey EXIT 1, 7 named failures, the enumeration failure named first
B′ co-occurrence removed drop the spelling requirement and the window test EXIT 1, 11 named failures — every legitimate-usage leg fires, plus the new window-9 boundary leg ⇒ the phrase alone is a noise gate
A′ line-scoped Math.abs(t - i) <= windowt === i EXIT 1, 10 named failures, including both os:check-comment legs and all three new discrimination legs
F cap relaxed to a floor enumeration → VOCABULARY.length >= 1, plus a smuggled row EXIT 1, 6 named failures — ⭐ and the enumeration failure is absent from them. The smuggled row is caught only as collateral damage on six unrelated fixtures. That is precisely why the cap must be an enumeration: the floor form still reds, but it never tells the author "you added a row without a survey"

Restore proven on every leg: blob d453fef6b44b9364c54c373756739404ebb1f8e9 == HEAD blob, git diff HEAD empty.

Gates

Union derived in this worktree with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack16 families (14 by
path + 2 by change kind), harvested with --commands so neither spelling nor section can be
dropped. Re-run in full at the final commit 4272212; exit codes captured by redirect before
any pipe, never from one.

All 16 measured at 4272212: 15 RAN-PASS, 1 NOT MEASURED. pnpm check:corpus-claim-drift at 4272212:

OK  self-test: … the shipped table is pinned as an EXACT ENUMERATION of its four row ids in order …
check-corpus-claim-drift: OK, no new claim sites beside a pinned spelling.
  Scanned: 224 .md/.mdx file(s) read across 2 root(s) — content/docs 190, skills 34.
  Rules:   4 row(s) — exists-key-presence 4, exists-portability 0, regex-retired 0, section-visiblewhen-unbound 0.
  Ledger:  2 baselined file(s) in scripts/corpus-claim-drift-baseline.json.

exists-key-presence 4 and Ledger: 2 are the pre-existing 2-file / 4-site figures, unchanged; the
three new rows report 0 each, which the gate publishes per row rather than hiding in a total.

Also green: check:ratchet-remedy-authority ("184 scripts swept … 12 mark the expanding remedy ⛔
MAINTAINER-ONLY"
), check:watch-hint-literal ("45 declaration(s) across 4 rostered name(s) … no
unrostered spelling"
), scripts/pm/bare-root-worklist.mjs --self-test ("57 live row(s) … none
stale, none missing, none contradicted"
), check:entry-guard, check:parse-guard,
check:agent-test-spelling, check:bash32-floor, check:cli-command-ids,
check:cross-package-test-inputs, check:pnpm-filter-targets, check-ci-filter-parity,
check-cross-package-test-inputs, check-shard-attestation.

NOT MEASURED (1, declared, ⛔ not a red): node scripts/check-test-completeness.mjs exits 3
— the script's own text says nothing was swept and that it is not a finding.

pnpm check:pm-dispatch-gates (the heavy convention-triggered family) was re-run to completion at
the final head after this PR was opened: EXIT=0, "✓ dispatch-gates self-test: 1240 cases pass."

Changeset: none — scripts/**-only, nothing published. skip-changeset applied.

One thing this PR deliberately does NOT fix

.github/workflows/lint.yml, above the check:corpus-claim-drift step, still reads "carrying the
$exists family ONLY — each further word (the retired $regex spelling, #13532's visibleWhen
claims) pays its own baseline on its own card."
Every clause of that becomes false when this merges.
.github/** is off this card's file surface by dispatch (lint.yml is a hot conflict file).
Filed as #14601 with the correcting wording and a note that it must land after this PR, not
before.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV


Generated by Claude Code

…aim ratchet

#13582 shipped `scripts/check-corpus-claim-drift.mjs` with exactly one row and a
self-test assertion pinning that, because a row is only as good as the survey of
legitimate usages someone did before adding it. This is the follow-up card that
pays three such surveys, one candidate at a time and none of them batched.

- `exists-portability` — the `(NoSQL)` gloss. A DIFFERENT claim from key
  presence (portability, not presence), hence a row of its own. Survey: 8 NoSQL
  hits over 7 corpus files; two of them sit within 4 lines of `$exists` and BOTH
  are legitimate — the repaired "portable, not a NoSQL-only operator" sentence
  and a skills column header — so the claim is the parenthesised gloss and an
  unnegated "NoSQL-only" / "MongoDB-only", never the word. 0 baseline entries.
- `regex-retired` — the retired `$regex` spelling. Survey: 5 hits over 3 corpus
  files, ALL legitimate retirement notices, so the claim is the live-operator
  phrasing and never the mention. 0 baseline entries.
- `section-visiblewhen-unbound` — #13532's section-binding claims. Survey: 88
  `visibleWhen` hits over 21 corpus files, 0 firing at any window from 4 to 40.
  Window 9, measured on the pre-repair tree: it is the smallest width reaching
  the binding-root table row, and 9..40 yield an identical ledger. 0 baseline
  entries.

The one-row cap moves to an EXACT ENUMERATION of the shipped row ids in order,
never a floor, with a discrimination leg proving a smuggled row still fails it.
Every row ships its pre-repair fixtures, its repaired/legitimate green legs and
a discrimination leg. The baseline file is untouched and byte-identical.

Part of #13745

Co-authored-by: Claude <noreply@anthropic.com>
…eration

The enumeration assertion records each shipped row's survey. The `regex-retired`
line said 5 hits; the measured figure over `content/docs` + `skills` (generated
`references/` excluded, as the gate excludes it) is 8 hit-lines over 3 files —
`deployment/troubleshooting.mdx` 1, `protocol/objectql/query-syntax.mdx` 6,
`releases/v17.mdx` 1. Every one of them is still a legitimate retirement notice
and the baseline cost is still zero; only the recorded number was wrong, and a
survey figure that is wrong in the record is worth exactly nothing.

Part of #13745

Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 2, 2026
@github-actions github-actions Bot added the size/m label Sep 2, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 2, 2026 14:49
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 4b9e1f3 Sep 2, 2026
39 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13745-corpus-claim-ratchet-rows branch September 2, 2026 15:31
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Second and further rows for the teaching-corpus lexical ratchet — NoSQL portability gloss, retired $regex, visibleWhen claims

2 participants