Repository navigation
fix(app-shell): Studio's validation "Runs on" row reads the spec's events contract (objectui#11923) - #11945
Merged
objectstack-fleet[bot] merged 4 commits intoOct 8, 2026
Conversation
…ents contract (objectui#11923) The row offered Create, Update and Delete from a local list, and showed a rule with no `events` key as running on nothing. It now offers the spec's events (read off `ScriptValidationSchema.shape.events`), shows an absent key as the spec's default, and a tick writes the full resulting list. The Delete label row, en and zh, goes with the box: nothing else reads it. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
…bjectui#11923) Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
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. Which half objected:
📦 Bundle Size Report
Size Limits
|
main 3c888c6 rolls back objectui#11931 and objectui#11932 (objectui#11956). One conflict, the panel's import block: the rollback drops the validationPresets import, this branch adds the @objectstack/spec/data import of ScriptValidationSchema beside it. Both kept: the spec import stays, the presets import goes. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
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
|
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.
Fixes #11923
Clause-②: no
Studio's validation-rule Runs on row now reads the spec's
eventscontract (eventsinBASE_VALIDATION_SHAPE): it offers exactly the events the spec declares, shows a rule with noeventskey as the spec's default, and a tick or an untick writes the full resulting list.Implemented by the os-dev agent of session
https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU(seatdomain:ui#2, claim comment 6056228085).What was wrong (measured on
main19a7348, spec 17.7.0)EVENTS = ['insert', 'update', 'delete']. Ticking Delete wroteevents: [..., 'delete'], whichObjectSchemarefuses (invalid_value, expectedinsertorupdate).eventskey opened with no box checked. The spec parses it to['insert', 'update'], and the server's rule validator readsr.events ?? ['insert', 'update']. Ticking Create wrote['insert']and stopped the rule running on updates.What changed
packages/app-shell/src/views/studio-design/ObjectValidationsPanel.tsx:H1, the spec is read, not copied.
@objectstack/spec/data17.7.0 exports no named events enum (noValidationEvent;BASE_VALIDATION_SHAPEis module-private). Its public entry does export every rule type's schema, sorunsOnContract()readsScriptValidationSchema.shape.events:.unwrap().element.optionsis what the row offers, and.parse(undefined)is what it shows for an absent key. The type is derived as well (ScriptValidationParsed['events'][number]). The read happens on first use, not at import, because the spec builds its schemas lazily. The new test pins that all six rule types' schemas agree on both values, so reading one type's schema is reading all of them. No spec export was added.H2, one display rule.
ruleRunsOn(rule)is the only place the row readsevents: an absent key gives the spec default, an array gives itself.writeRunsOnwrites every offered event whose box is checked after the click, in the spec's order. From the absent state, unticking Create writes['update'].An empty list, measured.
ValidationRuleSchemaandObjectSchemaboth acceptevents: []. The parse keeps it as[](it does not default), and the server's?? ['insert', 'update']does not replace it, so the rule runs on nothing. The last box can therefore be unticked. It writes[], and the row then shows no box checked, not the default.H3, a stored
delete. On base the panel showed a third box, Delete, checked, and the row's next tick kept it (['insert', 'delete', 'update'], still refused). Now:delete);eventsacross, as before), so nothing is dropped silently;This is the triage's own direction ("a tick writes the full resulting list" of the offered events) applied as written. It does not change what the server runs. No new copy was added.
H4.
NEW_RULE_EVENTS(objectui#11820) is untouched. The new test pins that a new rule is written witheventsequal to the spec's parsed default and shows both boxes checked.packages/app-shell/src/views/metadata-admin/i18n.ts: theengine.studio.rules.event.deleterow is removed from the designer table, en and zh (2 rows, net minus 2). Its only reader was the template key on this row, which now takes only the spec's events.Tests beside the panel:
ObjectValidationsPanel.runsOn-11923.test.tsx(new, 12 tests). Every write is parsed through the spec'sObjectSchema.ObjectValidationsPanel.newRuleWaits-11820.test.tsx: its one Delete is not checked assertion now asserts that no Delete box exists.ObjectValidationsPanel.celGate.test.tsx: one comment that cited the Delete checkbox is reworded.Verification (head
2ad86b2fb, after the merge ofmainf3a0488)All read on
2ad86b2fb(git rev-parse --short HEADafter the last commit). Each exit code was captured before any pipe.pnpm exec vitest run(repo root), on the six existingObjectValidationsPanel*.test.tsx, the newrunsOn-11923file andpackages/core/src/utils/__tests__/column-identity.ratchet.test.ts:Test Files 8 passed (8),Tests 72 passed (72), exit 0.pnpm --filter @object-ui/app-shell type-check(tsc --noEmit && tsc -p tsconfig.test.json): exit 0, 0error TS. The dependency closure was built first (turbo run build --filter='@object-ui/app-shell^...', 28/28).tsc -p tsconfig.test.json --listFilesOnlylists the new test file.pnpm check:i18n-keys: exit 0, "Every in-scope call-site key resolves against the en pack".pnpm check:i18n-designer-parity: exit 0, "Every en row has a zh row, and every shared row carries the same placeholders."pnpm check:i18n-drift: exit 0, "No designer-table en value changed in this range."pnpm check:spec-symbols: exit 0, "nothing cites a key its spec symbol does not declare".pnpm check:metadata-write-doors: exit 0, "OK 17 metadata write door(s) derived".node scripts/check-changeset-presence.mjs: exit 0, "this change declares 1 changeset(s): .changeset/11923-validation-runs-on.md".node scripts/check-changeset-no-major.mjs: exit 0, "No changeset declares amajorbump."pnpm check:new-line-citations: exit 0, "VERDICT new-cross-file-line-citations: 0 new citation(s)".pnpm check:control-bytes: exit 0, "check-control-bytes: OK".pnpm check:changeset-claimsandpnpm check:pending-changeset-literals: both exit 0. They were added because the diff adds a changeset.pnpm exec eslinton the five touched source and test files: exit 0, 0 errors and 1 warning. The warning isreact-hooks/set-state-in-effectinJsonField's re-sync effect, whose lines this PR does not change.Reverse validation, run once on commit
0b71e26e0:i18n.tswere checked out at base 19a7348. Both blobs were confirmed equal to base bygit hash-object.deletepins (base shows a third box, and writes['insert', 'delete', 'update']).[]pin, the refused-stored-rule control, the unrelated-edit pin and the new-rule pin.git checkout HEAD -- PATH, proven by HEAD blob hashes and an emptygit diff HEAD.Docs
content/docs/guide/console.md's Validations row does not name the Runs on events, and no doc sentence incontent/or a package README names them. Nothing became false, so no doc was patched.Acceptance notes
mainis red on the console eager-closure ceiling, which objectui#11937 is handling. This PR does not touch the budget. The panel stays behind Studio's lazy route (StudioRoute.tsxlazy-imports the surface). It does add one new value import from@objectstack/spec/data(ScriptValidationSchema). Its code is likely already in the build throughObjectSchema(clientValidation's dynamic import), butadvancedChunksroutes@objectstack/*into a named vendor chunk, and that chunk may gain one export entry. NOT MEASURED locally: no console build was run. Read this PR's own Bundle Analysis comment for the delta.FieldOptand the field-options memo). This PR does not touch them. It had not landed at the merge above.Generated by Claude Code