Pin @objectstack/* to 17.1.0 - #5529
Conversation
…oves Lockfile refresh only — every manifest already declared `^17.0.0`, which admits 17.1.0, so no dependency range changed. spec, client, formula and lint move in lockstep (a split resolution is what produced the dual-version spec graph that reddened check:spec-symbols in this repo's history). Green-keeping, each forced by the new build and nothing else: - data-objectstack: identity alias row for `icontains`, a canonical VIEW_FILTER_OPERATORS member as of 17.1.0 and itself a member of VALID_AST_OPERATORS. Unmapped, an authored `icontains` reaches the wire raw — the shape that shipped an unfiltered query in objectstack#3948. - app-shell palette: drop the now-stale `element:filter` exclusion, exclude the newly-declared `record:discussion` as the same renderer as the offered `record:chatter`. Nothing the palette offers changes. - check-spec-symbol-derivation DEBT: `ReferenceRailEntry`, which 17.1.0 started exporting under a name objectui already used. The shapes disagree on `icon` (spec's schema is $strict and refuses it), so reconciling is a contract call — filed as #5494. - check-action-forward-parity KNOWN_GAPS: `onSuccess` x4 declared surfaces. ActionRunner has honoured it all along; 17.1.0 supplied the missing half by declaring it authorable, so it became owed. Forwarding it is capability wiring — filed as #5493. check:eager-closure is NOT addressed here and is left red deliberately: the closure grows 129,270 gzipped bytes (3,871,997 to 4,001,267), 41,267 over a ceiling the maintainer ruled on today in #5468 — "the absolute aggregate ceiling stays as shipped", and "gate-strength policy is the maintainer's". Refs #5328
All four are forced by the new contract and nothing else. - plugin-view: map the newly-canonical `icontains` onto the builder's `containsCaseInsensitive` (the id that authors the spec's `$icontains`). The table is keyed by ViewFilterOperator, so this was a compile error, not a silent gap. Deliberately not folded onto `contains`, which would rewrite a case-insensitive filter as case-sensitive on the next save. - test-support tombstone judge: 17.1.0 retires `element:filter` WHOLLY — it leaves PageComponentType while its props schema stays in ComponentPropsMap with every key tombstoned. That is a retired block, not the "broken probe" the old floor assumed, so the floor now applies to blocks the enum still offers. The control key is searched for rather than taken from found[0], which had made it depend on sort order. - console registry parity: delete the two `targetVariable` exemptions, which went dangling-and-stale exactly as that file's docblock predicted they would at the next pin; pin the five `record:*` blocks 17.1.0 adds to ComponentPropsMap (37 entries to 42) using the same pin-dependent shape GA_ONLY_BLOCKS already established; exempt `record:reference_rail.entries`, an array of objects a flat scalar `inputs` carrier cannot express, whose editor also needs #5494's `icon` question settled first. Refs #5328
Three inventories that enumerate a spec vocabulary, all named by their own ratchets on the bump. - plugin-list `mapOperator`: explicit `icontains` arm. The emitted spelling equals the input, so `default` already returned it — but that file's header records relying on the AST set happening to accept a raw view spelling as exactly how it stopped discriminating in #3641, so the arm is written out and pinned in EXPECTED_AST_TARGET. - plugin-list tab presets: VIEW_FILTER_OPERATORS is 20, not 19. - core `SPEC_ACTION_KEYS`: add `onSuccess`. Diagnostic only — the list feeds warnOnUnknownActionKeys, so without it an author writing the key the spec now declares gets told it is unknown. The forwarding gap stays open and tracked (#5493). Refs #5328
❌ 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
|
PM review — holding at draft, blocked on a maintainer decisionReviewer of record: the spec@objectui seat ( The red gate is correctly red, and correctly not fixed here
The dev's citation checks out. I read #5490 directly: it records the maintainer's ruling of today verbatim as 「其他接受」 = A now + C next, B rejected, with the aggregate ceiling staying as shipped, and states in its own words that "Gate-strength policy is the maintainer's. ⛔ No seat should pick this." Refusing to raise it was the right call, and reporting it rather than quietly widening the budget is exactly the behaviour the gate exists to protect. Escalated with a four-axis analysis and three costed options: #5531. What I verified, beyond the report
On the out-of-scope findings#5493, #5494 and #5495 were filed rather than ridden in, and each was parked in the gate's own ratcheted ledger so the entry dies when the real fix lands. That is the right shape — a pin bump takes no riders. I have not re-graded them; grading is triage's. One judgement I specifically agree with: mapping VerdictNot ACCEPT, not REWORK — ESCALATE. The implementation is sound and I found nothing to send back. It waits on #5531. ⛔ Staying draft. Not flipped to ready, not queued, not auto-merged. Generated by Claude Code |
CI is fully in: 1 failure, and it is the escalated oneAll 22 check runs are now This upgrades the PR body's "everything else is green" from a self-report to a CI-verified fact. Green includes CI and the dev's local measurement agree exactly — the same numbers in different units:
One reading CI adds that the local run did not surface: the entry chunk is 151.1 KB against a 350 KB budget — comfortably inside. So this is not an entry-point regression; it is the statically-reachable closure across 52 chunks. That matters for the decision on #5531, because it means no amount of entry-point tidying reaches this — only reducing what console's first screen statically pulls in, or accepting the weight. Why I am not pushing a fix⛔ The only change that turns this green is raising the ceiling, and that is gate-strength policy — the maintainer's, per the ruling recorded on #5490 today ("Gate-strength policy is the maintainer's. ⛔ No seat should pick this."). This is precisely the case the gate was built for: the regression is larger than the 89 KiB incident that motivated it. Skipping, disabling, or re-baselining the gate to get green is off the table for this seat under any of the three options on #5531. The PR stays draft until that card is answered. No re-run requested: this is a deterministic budget computation, not a flake — a second run returns the same bytes. Generated by Claude Code |
…1.0 pin Maintainer ruling on objectui#5531: option A, raise the ceiling permanently together with the headroom assertion that guards it. MAX_EAGER_CLOSURE_GZIP_BYTES 3,960,000 -> 4,086,000 BASELINE.gzipBytes 3,881,609 -> 4,005,911 (52 of 508 chunks) BASELINE.commit 77f846a -> 4c1623c Both constants move in ONE commit by necessity, not preference: the guard is `MAX - BASELINE < REGRESSION_THIS_GATE_MUST_CATCH_BYTES`, so raising MAX alone leaves headroom near 200 KB and fails the gate's own unit test. That failure is the guard working. Headroom is 80,089 bytes = 2.00% of the baseline, holding the header's existing ~2% discipline, and 80,089 < 91,136 with 11,047 to spare. The substantive property is preserved, not just the inequality: an 89 KiB repeat of objectui#5266 measured from the new baseline lands at 4,097,047, still over the ceiling, so the gate is exactly as sensitive as it was. REGRESSION_THIS_GATE_MUST_CATCH_BYTES is deliberately UNCHANGED. That is the gate's sensitivity and the ruling did not touch it; a ceiling that rises while sensitivity relaxes is a gate retiring itself. The baseline was re-measured on this branch after merging origin/main (4c1623c), not copied from the earlier 4,001,689 reading — main moved 62 commits in between and carried the closure up by 4,222 bytes of its own. Header prose updated so the comments and the constants agree. Refs #5328
✅ 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
|
Fixes #5328
Pins
@objectstack/spec,@objectstack/client,@objectstack/formulaand@objectstack/lintto17.1.0, adapts the surfaces the new contract moves, and re-baselines the console eager-closure ceiling under the maintainer ruling on #5531. All gates green.The pin
A lockfile refresh, nothing else. All four packages move together — a split resolution is what produced the dual-version spec graph that reddened
check:spec-symbolsin this repo's history.@objectstack/spec17.0.017.1.0@objectstack/client17.0.017.1.0@objectstack/formula17.0.017.1.0@objectstack/lint17.0.017.1.0No manifest edit was required, confirming the card's assumption: all 38 declarations across 33 packages are
^17.0.0, a single distinct range per package, and^17.0.0already admits17.1.0. Two transitive@objectstack/*packages move in lockstep (core,sdui-parser), which is what keeps the graph single-version — counting the 17.0.0-pinned objectstack identity inpnpm-lock.yamlgives 0. One unrelated transitive re-resolved inside its own caret:@mongodb-js/saslprep1.4.13 → 1.5.0.motionis untouched, so PR #4958's lockfile change was not absorbed — the branch point is correct.Note on
pnpm update, because it does more than the card asked for: run against the four package names it rewrote all 31 manifests from^17.0.0to^17.1.0, and also silently re-indented two unrelatedcheck:*script lines in the rootpackage.json. Both were reverted and the install re-run, so the lockfile'sspecifier:fields still read^17.0.0whileversion:reads17.1.0. The diff is lockfile-only for the pin, as the card specified.Green-keeping
Each item is forced by the new build and nothing else. Every one was counter-probed against
origin/mainwith the 17.0.0 lockfile to confirm it is bump fallout rather than a pre-existing red.Fixed here — mechanical, one correct answer already pinned by existing evidence:
icontainsis a new canonicalVIEW_FILTER_OPERATORSmember (vocabulary 19 → 20) and is itself a member ofVALID_AST_OPERATORS. Three places needed it:data-objectstack's alias table (an identity row likecontains— unmapped, an authoredicontainsreaches the wire raw, the shape that shipped an unfiltered query in objectstack#3948);plugin-view's canonical-to-builder table, which is keyed byViewFilterOperatorand so failed to compile (mapped to the builder'scontainsCaseInsensitive, the id that authors$icontains— deliberately not folded ontocontains, which would rewrite a case-insensitive filter as case-sensitive on the next save); andplugin-list'smapOperator, given an explicit arm rather than left to thedefaultpassthrough that happens to work today.onSuccessadded tocore'sSPEC_ACTION_KEYS— diagnostic only. That list feedswarnOnUnknownActionKeys, so without it an author writing the key the spec now declares is told it is unknown.element:filterexclusion is dropped and the newly-declaredrecord:discussionexcluded as the same renderer as the already-offeredrecord:chatter. Nothing the palette offers changes.targetVariableexemptions deleted — they went dangling-and-stale exactly as that file's own docblock predicted they would at the next pin; the fiverecord:*blocks 17.1.0 adds toComponentPropsMap(37 → 42) pinned using the same pin-dependent shapeGA_ONLY_BLOCKSalready established.element:filterwholly — it leavesPageComponentTypewhile its props schema stays inComponentPropsMapwith every key tombstoned. That is a retired block, not the "broken probe" the old floor assumed, so the floor now applies to blocks the enum still offers, and the control key is searched for rather than taken from the first sorted entry.Recorded, not fixed — each is capability wiring or a contract call, which a pin bump does not take as a rider. Each uses the gate's own purpose-built, ratcheted ledger, so the entry dies the moment the real fix lands:
onSuccessis dropped by all four declared action surfaces →KNOWN_GAPS, filed asonSuccessis authorable as of @objectstack/spec 17.1.0 and honoured by ActionRunner, but all four declared action surfaces drop it one hop before the runner #5493.ActionRunner.ts:1197has honoured the key all along; 17.1.0 supplied the missing half by declaring it authorable (0 → 53 occurrences in the spec'sdist/**/*.d.ts), so it became owed for the first time.ReferenceRailEntryis now a spec-owned name →DEBT, filed asReferenceRailEntryis owned by @objectstack/spec as of 17.1.0 and objectui's local copy declares an extraiconkey the spec's strict schema refuses #5494. objectui did not fork it; 17.1.0 started exporting the name. The shapes disagree onicon, which the spec's strict schema refuses while this repo's renderer reads it.record:reference_rail.entries→UNPUBLISHED_EXEMPTIONS. Newly judged rather than newly missing: the registration has always published onlyhideEmpty, andentriesis an array of objects a flat scalarinputscarrier cannot express.One observation filed and not acted on: the palette advertises the pair under the legacy alias (
record:chatter) and now explicitly excludes the canonicalrecord:discussion— #5495.Worth recording, because it defeats the obvious check:
PageComponentTypehas 34 members before and after.element:filterleft asrecord:discussionarrived, so a member-count assertion cannot see this release at all.The eager-closure ceiling — re-baselined by ruling
The pin put the closure over the ceiling. The gate was right to refuse it, so the raise was escalated rather than taken locally; the maintainer ruled option A on #5531 — raise it permanently, with the headroom assertion that guards it.
MAX_EAGER_CLOSURE_GZIP_BYTESBASELINE.gzipBytesBASELINE.chunks/totalChunksBASELINE.commit77f846a8b4c1623c0cREGRESSION_THIS_GATE_MUST_CATCH_BYTESHeadroom is 80,089 bytes = 2.00% of the new baseline, holding the header's existing ~2% discipline, and 80,089 < 91,136 with 11,047 to spare.
Both constants move in one commit by necessity, not preference: the guard is
MAX − BASELINE < REGRESSION_THIS_GATE_MUST_CATCH_BYTES, so raisingMAXalone leaves headroom near 200 KB and fails the gate's own unit test. That failure is the guard working.The inequality is not the point on its own, so the substantive property was checked too: an 89 KiB repeat of the #5266 incident, measured from the new baseline, lands at 4,097,047 — still over the 4,086,000 ceiling. The gate is exactly as sensitive as it was.
REGRESSION_THIS_GATE_MUST_CATCH_BYTESis deliberately untouched. That is the gate's sensitivity, the ruling did not touch it, and a ceiling that rises while sensitivity relaxes is a gate quietly retiring itself.The baseline was re-measured on this branch after merging
origin/main, not carried over from the earlier 4,001,689 reading —mainmoved 62 commits in between and took the closure up 4,222 bytes on its own. Header prose was updated in the same commit so the comments and the constants agree; the77f846a8btable stays as the explicitly-labelled motivating measurement.For the record on where the bytes went: the release is ~930 KB larger uncompressed (
dist.mjs11,661,923 → 12,593,720) and essentially all of the gzipped growth lands invendor-objectstack-*.js, 1493.3 KB → 1619.5 KB. #5490 is the ruled follow-up that puts a per-chunk budget on exactly that chunk.Verification
Run on HEAD
4ad75fdfe, exit codes captured before any pipe.pnpm build— turboTasks: 43 successful, 43 totalpnpm type-check— turboTasks: 81 successful, 81 totalpnpm lint— turboTasks: 47 successful, 47 total(the full farm, not a narrowed run)pnpm test, all four shards: 1,819 files, 23,010 passed, 1 skipped, 0 failedscripts/__tests__/check-eager-closure-budget.test.ts: 27 passed, includingthe ceiling itself > passes on the measured baseline, with headroomandthe ceiling itself > would have failed on the regression it exists to catchcheck:*gates PASS,check:eager-closureamong themcheck:eager-closure, its own verdict line, before and after:Two other gates' own lines:
Generated by Claude Code