refactor(data-objectstack): rename the spec-named filter-operator fold (objectui#7265 data-objectstack slice) - #9527
Merged
os-tesla merged 1 commit intoSep 14, 2026
Conversation
…d (objectui#7265 data-objectstack slice) `packages/data-objectstack/src/index.ts` declared a module-local `normalizeFilterOperator` — the exact name `@objectstack/spec/ui` exports and most of this monorepo imports. Renamed to `toAstFilterOperator`, the codomain that makes it a different function; the ledger in `scripts/check-spec-symbol-derivation.mjs` is regenerated with `--ledger`. RENAME rather than BIND, decided by measurement at the RESOLVED 17.4.0 pin: the spec's folds an authored spelling to the canonical VIEW vocabulary so `ViewFilterRuleSchema`'s enum can judge it, this one translates the same input into the server's filter-AST symbols — `equals`/`before` against `=`/`<`. The measurement overturned the seeding note in both directions. The `?? op` tail is common ground, not the divergence: both hand an unrecognised string back unchanged. The tail does diverge on the NON-string arm, and the other way round — the spec returns it verbatim where this package returns `null`, which `objectFilterEntryToAST` reads into a `MalformedFilterError`. Binding would have widened what this adapter accepts onto the wire. Behaviour unchanged; the declaration was never exported. The stale entry-count figure in the gate's `DEBT_ISSUE` note is deleted rather than re-derived (AGENTS.md #9), and the sentence now names `--ledger` / `--claim-ledger` and the run banner instead. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011QreXiyMEqKLN4U5daMPVa
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-tesla
marked this pull request as ready for review
September 14, 2026 21:07
os-tesla
deleted the
claude/issue-7265-data-objectstack-filter-operator
branch
September 14, 2026 21:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of objectui#7265 — the
@object-ui/data-objectstackslice of thecheck:spec-symbolsDEBT burn-down. One group remains (@object-ui/plugin-detail), so this carries no closing keyword by design.What moved
packages/data-objectstack/src/index.tsdeclared a module-localnormalizeFilterOperator— the exact name@objectstack/spec/uiexports, and which this monorepo's view layer imports from there (viewFilterFold,filter-converter,ListView,UserFilters, the FilterBuilder). It is nowtoAstFilterOperator; rule 1'sDEBTblock is regenerated mechanically with--ledger.Route: RENAME — and the measurement that refused BIND
Re-measured against the RESOLVED pin,
@objectstack/spec@17.4.0(declared range^17.2.0; "same behaviour" is a statement about a version exactly as "byte-identical" is).The two functions take the same input and land in different vocabularies:
normalizeFilterOperatoreqequalsequalsequalsgreater_thangreater_thanbeforebeforeThe spec's folds an authored spelling to the canonical VIEW vocabulary so
ViewFilterRuleSchema's enum can judge it (it is thez.preprocessstep onViewFilterRuleSchema.operator). This package's translates the same input into the server's filter-AST SYMBOLS. Same input, different codomain — so binding would have changed what goes on the wire for most of the operators a stored view can carry. A function also has no derive-in-place form: rule 1 records every function declaration withderived: false, which leaves exactly three exits (delete and import, rename, waive). RENAME removes the collision instead of excusing it, and the declaration was never exported, so renaming costs nothing outside the file.What the measurement overturned — the
?? optailThe dispatch's hypothesis was that the spec's version REFUSES where this one is lenient. Measured, it is the other way round, in both halves:
normalizeFilterOperator('totally_unknown')returns'totally_unknown', and so does this package's table lookup. "The spec refuses instead" is not a difference that exists.return op as string, so a non-string comes straight back (42returns42,nullreturnsnull). This package returnsnullthere, andobjectFilterEntryToASTreads thatnullone line later into aMalformedFilterError. Binding the spec would therefore have WIDENED what this adapter accepts onto the wire — a number in the operator slot of a tuple it is about to send, instead of a 400-shaped refusal.That refusal is already pinned end-to-end, through the real adapter and both
find()routes, by theoperator: 42case inpackages/data-objectstack/src/filter-entry-translation.test.ts. Clause-② staysno: nothing this PR does changes what the package sends or what it accepts as an operator — the?? optail, thenullarm and every row ofFILTER_OPERATOR_ALIASESare byte-for-byte unchanged. The trip-wire was checked, not assumed.The INTERMEDIATE reading (site edit, not a ledger edit)
The gate was run after the site edit and before regenerating the block. Real output:
The gate's trailing clause is elided on purpose: it spells a closing keyword immediately before this card's number, and GitHub's linked-issue parser does not read sentences or spare fenced blocks — quoting it verbatim here risks closing a card that still has a group left. Read the full wording in the gate's source (its stale-entry message). Seat attribution for this run, as prose so it survives an edit:
session_011QreXiyMEqKLN4U5daMPVa.exit 1. That message names the entry as stale — which is only reachable if the declaration left the tree first.Ledger, before and after
Banner before (at
bbc9dc3):18 declared dialects, 2 untriaged collisions in 2 packages.—exit 0Banner after:
18 declared dialects, 1 untriaged collision in 1 packages.—exit 0--ledgerre-run after the splice is byte-identical to what was spliced in (idempotent).--claim-ledgerregeneratesCLAIM_DEBTbyte-identically across this change — the declaration that moved carried no spec-alignment claim, so rule 2's block is untouched.The stale figure, deleted rather than re-derived
The
DEBT_ISSUEnote carried a figure written by an earlier slice and overtaken by every slice since: it said how many entries objectui#7265 had burned out of each block. Per AGENTS.md #9 it is deleted, not re-derived to a new number. The sentence now names the routes (BIND / RENAME / ALLOW) and the instruments (--ledger,--claim-ledger, and the run banner) and carries no count at all.One extra deletion, declared: the same note's preceding clause read
its 18-entry block is live, and the sentence I was asked to rewrite endedonly the COUNT above moved— pointing straight at it. Leaving the adjective while writing "no count is written here" would have made the replacement contradict the line above it, so18-entrywent too. It isits block is still livenow; the argument (the anchor is closed while the block is not empty) is intact.Pins that FAIL before and pass after
Both legs were run from the committed state, mutation proved on disk by marker counts and blob hashes, restored with
git checkout HEAD -- PATHand confirmed by an emptygit diff HEAD. Neither leaves a file behind.Leg 1 — the site. Restored
packages/data-objectstack/src/index.tsfrom the pinned base commit (marker counts movedtoAstFilterOperator2 to 0,function normalizeFilterOperator0 to 1; on-disk hash changed):Exactly one assertion fails, and it is the site assertion. Green on the branch:
15 passed (15).Leg 2 — the figure. Put the deleted figure back:
It catches BOTH re-introduced counts, which is what makes it a ratchet against a helpful re-derivation rather than a spelling pin.
Ratchets left behind
packages/data-objectstack/src/spec-symbol-batch6.test.ts— appended to its existingRENAMEStable rather than duplicated into a new file. Both directions: the spec must not come to owntoAstFilterOperator, and it must still ownnormalizeFilterOperator(if it stops, the rename's reason is spent and the plain name can be taken back).scripts/__tests__/spec-symbol-ledger-data-objectstack-7265.test.ts— the sibling shape of the core / app-shell / types / components pins. Its controls include one this slice had to add: a fixture that imports the spec's function and re-declares a wrapper under the same name is STILL flagged, which is the assertion that records why a function's route set is smaller than a type's.packages/data-objectstack/src/filter-operator-ast-parity.test.ts, beside the table it is about. The divergence set is DERIVED from the spec's own vocabularies and enumerated, never counted, with a lit control (containsagrees on both sides) so "they differ" cannot be vacuous.Verification
pnpm exec vitest run packages/data-objectstack/ packages/plugin-view/ scripts/__tests__/—Test Files 265 passed | 2 skipped (267),Tests 5910 passed | 2 skipped (5912).pnpm --filter @object-ui/data-objectstack --filter @object-ui/plugin-view run type-check— exit 0, both scripts echoed. This package'stsconfig.jsoncompiles its test tree (confirmed with--listFiles: both edited test files are in the program), so the pins are checked bytsc --noEmittoo.pnpm run type-check:scripts— exit 0 (covers the newscripts/__tests__pin).check:spec-symbolsexit 0,check:changeset-presenceexit 0,changeset:checkexit 0,check:new-line-citations0 new citation(s),check:control-bytesOK,check:test-path-rootsOK.check-governed-queue-guard --testover every changed path:NOT GOVERNED — none matched. Ordinary review route.eslint --no-inline-config --format jsonover the six changed files reports6files linted,4 errors / 130 warnings, all inpackages/data-objectstack/src/index.ts. The same command over that file at the base commit reports the same4 errors / 130 warnings, same fourno-consolerules, shifted by exactly the 30 lines this PR adds: zero new findings. Type-aware linting is not configured ineslint.config.js(noparserOptions.project/projectService), so this diff cannot move the verdict of any file it does not contain. The repo-widepnpm lintis CI's run, not this one.Acceptance notes
check:changeset-claims(report-only) flagged four pending changesets whose bodies namescripts/check-spec-symbol-derivation.mjs. Read, as the gate asks. This PR falsifies none of them — three describe derivation forms and rule 2's comment attachment, all still accurate. The fourth,.changeset/6286-explain-batch-cap-from-spec.md, says "both of its scanners skip non-exported declarations"; that sentence is already false, and was made so by objectui#6291 widening rule 1, not by this branch. It is another card's pending body and it publishes verbatim into the CHANGELOG — reported rather than edited here.Noted, not filed:
scripts/__tests__/check-spec-symbol-derivation.test.tscarries a comment sayingisContextTokenandnormalizeFilterOperator"are the live instances … both DEBT entries today". The first half went false with the@object-ui/coreslice; this PR takes the second. Not repaired here on the same reasoning the components slice declined the figure — another slice's paragraph, not this slice's file moment. Taker: the@object-ui/plugin-detailslice, which empties the block and inherits the whole sentence.Departure from the dispatch, declared
The dispatch's file surface named the site, the gate script and the parity ratchet. One file outside it is in this diff:
packages/plugin-view/src/config/__tests__/view-operator-builder-parity.test.ts, whose header citeddata-objectstack'snormalizeFilterOperatorby symbol name. A rename that leaves dangling citations is an incomplete rename, and AGENTS.md #11's whole thesis is that the person who moves the thing never sees the citation — this seat did see it. One comment, no behaviour, andplugin-viewis in the verification set above because of it.Do not enqueue; draft by instruction, and auto-merge is deliberately not enabled.
Generated by Claude Code
Generated by Claude Code