Skip to content

docs(driver-memory): the $eq exemption's note says the null-list cells were RULED, not held - #16103

Merged
zhuangjianguo merged 2 commits into
mainfrom
claude/issue-15480-eq-exemption-comment-ruling-state
Sep 6, 2026
Merged

docs(driver-memory): the $eq exemption's note says the null-list cells were RULED, not held#16103
zhuangjianguo merged 2 commits into
mainfrom
claude/issue-15480-eq-exemption-comment-ruling-state

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #15480

What was false

The $eq-only exemption above checkCondition's no-value guard justified its operator-shaped spelling by naming the cells it declined to move — $in: [null] / $nin: [null] — as "#13357's cells, needs-user-decision, held for the maintainer".

That stopped being true on 2026-08-31. The maintainer ruled those cells (option C, #13357) and the shapes are now REFUSED at the contract's validation entrance (assertListComparandShapes), with the negative pin memory-null-list-member-unreachable.test.ts.

This is not wording polish. needs-user-decision is a live protocol state with a named reader — the maintainer's inbox. A source comment asserting a cell sits in that state, when the door two packages over refuses the shape by ruling, invites the next author (human or AI) to re-escalate a settled question into the most expensive queue in this repo.

The four sites, re-derived by text on this branch's base (not by line number)

The card measured at e8c7956c4 and triage re-took the readings at 5315098; main has moved many times since, so all four were re-derived by symbol/text at 1157e7b72:

site current line text
the stale comment packages/drivers/driver-memory/src/memory-matcher.ts:312 and those are #13357's cells, needs-user-decision, held for the
spec ruling record packages/spec/src/data/filter-comparand-shape.ts:92 ## Refused BY RULING, 2026-08-31: a null list member (#13357) (with :502 naming "the null-member carve-out (2026-08-31 ruling, #13357)" and nullListMemberError thrown at :509)
spec CHANGELOG packages/spec/CHANGELOG.md:2973 e398863: feat(spec): refuse nullin list-comparand positions —$in/$ninmembers and$between bounds (#13357, #13495)
sibling header, same package memory-matcher-null-value-and-comparand.test.ts:56 already past tense: They were needs-user-decision when #13494/#13495/#13549 landed;

The adjacency is the card's sharpest point and it holds, measured: the stale note sits at memory-matcher.ts:309-313, and the same file states it correctly at :343 — the $gte: null note's "the 2026-08-31 ruling had refused the three siblings". Thirty-four lines apart, in one file, and the stale one is the one an author reading the guard arrives at first.

What changed, and what deliberately did not

The parenthetical is re-pointed the way memory-matcher-null-value-and-comparand.test.ts:56 and filter-comparand-shape.ts:92 already do: past tense for the held state, then the ruling, the door, and the negative pin.

The exemption's REASONING is unchanged, deliberately. Writing the exemption over the operator rather than over "the comparand is null" is still right — a rule spelled over the VALUE would still reach arms whose no-value answer is ruled elsewhere. Only the cells' state moved: refused at the door, rather than held. The note now says that, and adds one sentence saying explicitly that the ruling is not a licence to re-spell the exemption — the opposite error, which a reader who now knows the cells are decided could otherwise make.

Nothing behavioural moves. No arm, no guard condition, no behaviour. No second pin was added: memory-null-list-member-unreachable.test.ts already covers that door.

Comment-only, proven

Every added and removed line in the diff is a // comment. Mechanically:

git diff -U0 -- packages/drivers/driver-memory/src/memory-matcher.ts \
  | grep -E '^[+-]' | grep -v '^[+-][+-][+-]' | grep -vE '^[+-]\s*//'

prints nothing — no executable line moved. Diff is 1 file changed, 18 insertions(+), 2 deletions(-).

Clause-② — no, both limbs, instrument run

Both limbs of the test — does any exported symbol or signature move, and is any request newly accepted or rejected — are no. The diff is prose, and this section says so rather than skipping the instrument.

The instrument was run with the corrected resolution: every declaration file the package publishes, resolved from its exports map and files[], not the root dist/index.d.ts alone. @objectstack/driver-memory declares files: ["dist", "README.md", "CHANGELOG.md"] and a single . export (types ./dist/index.d.ts, import ./dist/index.mjs, require ./dist/index.js); dist/ holds exactly six files, all six were hashed.

Procedure: build at head → swap memory-matcher.ts back to 1157e7b72 → prove the mutation on disk (anchored grep counts flipped 1/0, git hash-object 2644c3bbd vs HEAD blob 4abcecf2d) → rebuild → hash → restore git checkout HEAD -- ... → prove the restore (blob back to 4abcecf2d, git diff HEAD empty, git status --porcelain empty) → rebuild → confirm the head snapshot reproduced byte-for-byte (a determinism control, so the one difference below is signal and not build noise).

published file head vs base
dist/index.d.ts identical
dist/index.d.mts identical
dist/index.js identical
dist/index.mjs identical
dist/index.js.map differs
dist/index.mjs.map differs

The only movement is the two sourcemaps, whose embedded sourcesContent carries the comment text. No executable byte and no declaration byte moves.

Why skip-changeset and no changeset file

skip-changeset means "this PR declares no release of its own" (pr-automation.yml's changeset-check), and here that is a measurement, not a judgement: against built dist/**/*.d.ts and dist/**/*.{js,mjs,cjs} the published surface is byte-identical (table above), and content/docs/** is untouched. Nothing ships. The label is applied on this PR.

Verification

Everything below was run at 1ea619613, this PR's head (the merge of origin/main 33e939ff3), after the final commit.

  • pnpm --filter '@objectstack/driver-memory^...' build — exit 0 (dependency closure built first, so the test run is not reading stale dist).
  • pnpm --filter @objectstack/driver-memory build — exit 0.
  • pnpm --filter @objectstack/driver-memory test — exit 0. Test Files 43 passed (43), Tests 1096 passed (1096). Identical to the pre-merge run at 97923fdec (43/1096), i.e. the package's suite is unchanged before and after.
  • pnpm --filter @objectstack/driver-memory typecheck — exit 0 (tsc --noEmit plus tsconfig.typecheck.json, so the test files are compiled too).
  • Gate family, re-derived after the final commit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. Its provenance line reads derived from the tree of 'objectstack-ai/objectstack' at commit 1ea619613, change set 1 path(s), and none of the commit(s) this tree can SEE touched what this answer derives from. 38 runnable families; 37 pass.
  • pnpm check:dual-build-cjs-loads — NOT MEASURED, not red. It exits 3 with PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ (80 packages listed, none of them driver-memory), i.e. it wants a whole-repo pnpm build. That is CI's run. Declared narrowing, with the reason it cannot move: this gate reads packages' built CJS/ESM output, and this diff's own package emits byte-identical dist/index.js, dist/index.mjs, dist/index.d.ts and dist/index.d.mts (measured above), while no other package's source is touched at all.

pnpm lint — a declared narrowing, measured

The repo-wide eslint . --no-inline-config sweep is CI's run; what is delivered here is a narrowed one plus the proof the narrowing excluded nothing:

  1. Population read from eslint's own config, not guessed: eslint.config.mjs states, with its own measurement and positive control, that this repo "runs one eslint.config.mjs, which never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" — confirmed by grep: no parserOptions.project and no projectService anywhere in it.
  2. File count read from --format json: npx eslint --no-inline-config --format json packages/drivers/driver-memory/src/memory-matcher.ts linted 1 file, 0 errors, 0 warnings, exit 0.
  3. Invariance for untouched files: with type-aware linting off, every file's verdict is a function of its own bytes; this diff changes the bytes of exactly one file, so no untouched file's verdict can move.

Bytes

pnpm check:nul-bytes passes, and the changed file was additionally self-scanned for control characters (grep -naP over the C0 set plus DEL) with no hit.

Not a duplicate

#13357, #13494, #13495 and #13549 are all in a closed state — they are the rulings and the behaviour repairs. None of those cards is "the comment recording the decision state is now false". This is also not folded into #14426: that card's fence is explicit ("the #13553 guard's comment only", three named prose sites), and this text is a fourth site outside it, present at PR #15464's merge-base.


Generated by Claude Code

…lls were RULED, not held (#15480)

The `$eq`-only exemption above the no-value guard justified its
operator-shaped spelling by naming the cells it declined to move —
`$in: [null]` / `$nin: [null]` — as "#13357's cells,
`needs-user-decision`, held for the maintainer". That sentence stopped
being true on 2026-08-31, when the maintainer ruled them (option C) and
the shapes became REFUSED at the contract's validation entrance
(`assertListComparandShapes`), with the negative pin
`memory-null-list-member-unreachable.test.ts`.

This is not wording polish. `needs-user-decision` is a live protocol
state with a named reader — the maintainer's inbox. A source comment
asserting a cell sits in that state, when the door two packages over
refuses the shape by ruling, invites the next author to re-escalate a
settled question into the most expensive queue in this repo.

The same package already stated it correctly, twice: the `$gte: null`
note 34 lines below in this file ("the 2026-08-31 ruling had refused the
three siblings"), and the sibling header in
`memory-matcher-null-value-and-comparand.test.ts` ("They were
`needs-user-decision` when #13494/#13495/#13549 landed"). So the package
stated the decision state two ways, and the stale one is the one an
author reading this guard arrives at first.

The exemption's REASONING is deliberately unchanged: writing it over the
OPERATOR rather than over "the comparand is null" is still right,
because a rule spelled over the VALUE would still reach arms whose
no-value answer is ruled elsewhere. Only the cells' state moved —
refused at the door, rather than held. The note now says so, and says
explicitly that the ruling is not a licence to re-spell the exemption.

Comment-only. No arm, no guard condition, no behaviour: every added and
removed line in the diff is a `//` comment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
…ent-ruling-state

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions github-actions Bot added the size/s label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 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
  • 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 — 8 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 b3820c3e20c2d57987e78ea6ab7f39b4780278d6packageMentionDocs.

Which tree this was computed on

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

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

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

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 6, 2026 01:59
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 0ef4f80 Sep 6, 2026
39 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-15480-eq-exemption-comment-ruling-state branch September 6, 2026 02:43
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