Repository navigation
studio: risky or no-op defaults — New action defaults to sandboxed JS, New hook can target every object incl. sys_* without warning, a new validation rule is created as a no-op #11820
Description
Activity
- addedenhancementNew feature or requestNew feature or requestdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portal
on Oct 7, 2026 objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsPath: write metadata — Studio's new-item defaults | 缺项 | P3
Triage: first grade,
enhancement·priority:p3·domain:ui·area:studio·pm:queue. Filed as ②, but routed as a Studio UX defect: what Studio offers and saves by default changes no contract. Direction: the body's option ATriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-07T17:45Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in the Studio create flows for actions, hooks and validation rules (
studio-design/ObjectValidationsPanel.tsxfor rules) ⇒domain:ui; rationale: a Studio or console surface in objectui. Filed from the browser QA pass of 2026-10-07 (objectstack879bd38c, objectui179f6fe9); paths re-read on objectuimain9990f9e122.- Why not the decision box: these are Studio defaults; no server rule changes. Why p3: a no-op rule is saved, and a broad hook target is offered without a word.
- Direction:
- a new validation rule is not saved until it has a condition, and starts with Create + Update checked
- a new hook targets the current object; other packages' objects and platform objects sit in their own group with a warning about reach
- a new action defaults to a declarative type
- ⛔ This card changes only what Studio offers and says. Whether the server binds an authored hook to a platform object is not measured here, and it is a security boundary, so it is the maintainer's. carrier: the claimant reads that path and reports in Acceptance notes; triage routes what it finds.
Clause-②: no. Patch changeset in objectui.
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01CGZy1BGCjdN5cXqL9cnvB8
Account:os-support-ai
Branch:claude/issue-11820-studio-safe-defaults
Worktree:objectui-issue-11820
Domain:domain:ui
Seat:domain:ui#3
File surface (on5e446ab), underpackages/app-shell/src/views/:studio-design/ObjectValidationsPanel.tsx: a new rule's defaults (condition: 'false'at about:162–:177) and when a new rule is first saved;studio-design/ObjectHooksPanel.tsxandmetadata-admin/inspectors/HookDefaultInspector.tsx: a new hook's target default and the target picker's grouping and warning (ALL_OBJECTSabout:69, the toggle about:224);studio-design/ObjectActionsPanel.tsx: a new action's default type (type: 'script'about:170);- new
engine.studio.*/engine.inspector.hook.*rows ofmetadata-admin/i18n.ts(en and zh) for the warning and group headings; - the tests beside these, and
.changeset/11820-*.md.
⛔ Not on it: what the server binds or refuses for a hook on a platform object (triage
6043523725: a security boundary, the maintainer's);packages/i18n/**;packages/components/src/ui/**. Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier)
Clause-②: no
Responsibility:objectui app-shell Studio: a new validation rule autosaves as a no-op (condition false, no event), a new hook offers every object including sys_* with no word about reach, and a new action starts as sandboxed JS | the platform path: each panel already owns its create defaults and its picker; only what Studio offers and saves first changes | every author adding a rule, hook or action to an object in Studio
Thread-read: 6043523725
Serial constraints cleared: seat 2's objectui#11781 (which touchedObjectValidationsPanel.tsx's read-only look) has landed. No in-flight claim in the lane names these four files, and no open objectui PR touches them (read 2026-10-08T05:27Z).Why
Clause-②: no: Studio defaults and copy inside@object-ui/app-shell, with rows in the designer's own string table. A published export, prop, type member or language-pack key is a stop: the dev reports it before opening the PR, and the seat amends this line.
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11820,
"status": "done",
"branch": "claude/issue-11820-studio-safe-defaults",
"pr": "#11918",
"session": "session_01CGZy1BGCjdN5cXqL9cnvB8 — subagent mode, the dispatching seat's id (domain:ui#3, PM loop round 3)",
"premise_still_valid": true,
"summary": "Draft PR #11918 (head 54f36e2, Fixes #11820, Clause-② line copied) implements option A in @object-ui/app-shell only, all on the claim's file surface. Validation rules: a new Script/Cross-field/Conditional rule is held in the panel (listed, marked 'Not saved — needs a condition', with the objectui#11786 hold's line and input hint) and written to the draft only once it has a condition (a Conditional also needs its Then rule's); every new rule starts on Create + Update; an existing rule's type switch keeps the valid 'false' placeholder. Hooks: ObjectHooksPanel provides the package's objects (loadPackageSurfaces, published + draft) through a module-internal HookTargetScopeContext, and HookDefaultInspector splits the picker into 'This package' and 'Other objects' (other packages' and sys_* alike) with a reach warning; 'All objects ()' gets one too. Actions: a new action is operation 'update' + patch {} (type left to the spec default), no sandboxed JS. Premise notes: the hook half 'targets the current object' was already true on 5e446ab (addHook seeds object: objectName) and is now pinned; the card's 'no Runs on event checked' rule did run on insert+update (spec default, measured) — its no-op came from condition 'false' alone. Zone 2 #1 measured: the pillar's hold (objectHeldEdit in metadataError.ts, Show-me routing in StudioDesignSurface.tsx, which objectui#11823 step 2 holds) is off this surface, so the panel holds the unsaved rule itself and reuses the hold's wording; stated cost: leaving Rules before a condition drops the rule, and Publish does not wait for it. Zone 2 #3 measured: update_field is not an ActionType; url/flow/modal/api/form are refused without target; operation 'update' + patch {} is the one declarative skeleton that parses. CI at report time: 42 check runs, 21 success, 3 skipped, 18 in_progress, 0 failed (one read, not awaited).",
"tests": "All on 54f36e2 via os-verify-lock (slot os-dev-11820), from the worktree root. (1) pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/studio-design/ — 'Test Files 122 passed (122)', 'Tests 762 passed (762)'. (2) pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/inspectors/ + 12 grep-derived files outside studio-design/ and metadata-admin/inspectors/ (ConditionWidget.conditionSubjects, ResourceEditPage.conditionScope, src/tests/select-placeholder-literal-11252, data-objectstack draft-envelope + metadata-client.get-envelope, 3 packages/i18n key tests, scripts/tests check-doc-component-types / check-i18n-call-site-keys / check-i18n-dead-keys / vite-dts-explicit-extensions) — 'Test Files 155 passed (155)', 'Tests 2394 passed | 1 skipped (2395)' (skip = pre-existing spec-gated skipIf in flow-node-config.spec-reconciliation). Declared narrowing: app-shell has 1133 test files; not run here = the rest of metadata-admin and app-shell, none of which names a changed module or a changed i18n string (git grep); blind spot = a test reaching a changed module only through a registry/barrel without naming it; full pnpm test is CI's. (3) pnpm --filter @object-ui/app-shell type-check ('tsc --noEmit && tsc -p tsconfig.test.json') exit 0 after building the dependency closure (turbo build --filter='@object-ui/app-shell^...' --concurrency=2: 28/28 cached); tsc -p tsconfig.test.json --listFilesOnly lists all 6 changed test files; run on the working tree later committed unchanged as 54f36e2. (4) eslint on the 11 changed .ts/.tsx files: 11 files judged (--format json), 0 errors, 4 warnings (1 new react-refresh/only-export-components on the exported context; 3 pre-existing react-hooks/set-state-in-effect); narrowing proof: population = eslint.config.js over the repo, no type-aware linting configured and no custom eslint-rules/ reading other files, so the diff cannot move an untouched file's verdict. (5) Ablation via objectstack scripts/ablation-replace.mjs WRAP mode (no build: the pins import the panel from src by relative path): addRule's wait replaced by the old immediate write with condition 'false' — anchor count 1 to 0, blob 21757a5a4e64 to da230345381a, marker grep 1 on disk; ObjectValidationsPanel.newRuleWaits-11820 / .whenKey / .test went red 'Tests 14 failed | 15 passed (29)', all assertions (13 AssertionError, 1 jest-dom toBeChecked), no crash; restore proven: blob after restore 21757a5a4e64 == HEAD blob, git diff HEAD empty. Direction observed: red (as expected).",
"mcp_calls": "0",
"api_writes": "3 REST writes through the fleet relay (objectstack-fleet[bot]): pr_create POST /repos/objectstack-ai/objectui/pulls (draft #11918, read-back 12535 bytes sent == stored); assign POST /repos//issues/11918/assignees os-support-ai (label-write.mjs, read-back MATCHES); comment POST /repos//issues/11820/comments (this os-dev-report). Not REST: git push x2 (empty-branch probe, then 54f36e2). Reads only otherwise (gh api GET of the card, comments, PR, check-runs).",
"open_questions": [
{
"question": "The unsaved-rule hold lives in ObjectValidationsPanel, not in the Data pillar's objectui#11786 hold. Move it onto objectHeldEdit later?",
"options": [
"A — keep the panel hold (this PR): no StudioDesignSurface.tsx edit; cost: leaving Rules or the object before a condition drops the rule, and Publish does not wait for it",
"B — follow-up card: put an empty-guard rule in the draft and extend objectHeldEdit (metadataError.ts) + the held notice's Show-me routing to the Rules view (StudioDesignSurface.tsx): survives view switches and Publish refuses, but holds every other edit of the object while the rule lacks a condition"
],
"recommendation": "A for now, B only if authors report losing unsaved rules: business need — the loss is a rule with no condition yet, which saves nothing anyone runs; long-term — B is the one hold mechanism, but it needs StudioDesignSurface.tsx, which objectui#11823 step 2 holds; AI-error axis — both write nothing the spec refuses; startup focus — A adds no surface beyond this card."
},
{
"question": "The 'What it does' path from the new default back to a script is refused until a language is picked (ActionDefaultInspector, off this surface). File it before this merges?",
"options": [
"A — the seat files it as its own card (ActionDefaultInspector.commitOperation seeds no body; patchBody writes no language) and lets this PR land",
"B — hold this PR until that card lands"
],
"recommendation": "A: the defect predates this PR (any update action switched to script hits it) and picking 'Sandboxed JS (L2)' explicitly gets through today; holding the safe default behind it buys nothing."
}
],
"out_of_scope_findings": [
"class: a · reach: named producer — Studio Actions, New (now an update action), 'What it does: Run a script', type a body: ActionDefaultInspector.commitOperation clears operation/patch and seeds no body, patchBody writes { source } without language; ActionSchema refuses the switched action at 'body' ('A script action requires either an inline body … or a target') and, after typing, at 'body.language' ("Invalid discriminator value. Expected 'expression' | 'js'") until a language is picked — measured through the spec's ActionSchema on the shapes those two functions write · this PR makes it the path for every author who wants a script action · dedupe words: action what it does run a script body language refused; commitOperation seeds no body; update action switch to script",
"class: b · reach: named producer — ObjectValidationsPanel's 'Runs on' row (EVENTS includes 'delete') writes events [..., 'delete']; ObjectSchema.safeParse answers invalid_value at validations.0.events.N ('expected one of "insert"|"update"') · Seam: spec:BASE_VALIDATION_SHAPE.events (enum insert|update, 'deleteis intentionally absent') → renderer:ObjectValidationsPanel Runs-on checkboxes · not fixed here: a refused value offered is not this card's no-op-default class · dedupe words: validation rule runs on delete checkbox; events delete refused; studio rules delete event",
"class: b · reach: named producer — a rule with no 'events' (authored in code or by AI) opened in Studio shows no Runs-on box checked, while ValidationRuleSchema parses it to ['insert','update'] (measured); ticking 'Create' writes ['insert'], narrowing a rule that ran on both · Seam: spec:BASE_VALIDATION_SHAPE.events default → renderer:ObjectValidationsPanel Runs-on checkboxes · dedupe words: runs on checkboxes absent events default; validation events default insert update display",
"carrier: 承接者:无 · noted, not filed — ConditionBuilder's empty state 'Always — no condition.' renders at the validation guard (a required fail condition) right above the 'Required' hint on a new rule (polish, ConditionBuilder.tsx off this surface)",
"carrier: 承接者:无 · noted, not filed — hook events are labelled with raw camelCase names (beforeInsert, afterUpdateMany); the designer string table has no label pattern for them, so none was invented (polish, per the dispatch)",
"carrier: 承接者:无 · noted, not filed — an update action written by the inspector's 'What it does' carries explicit type 'script' (Actions list shows a SCRIPT chip) while the new default leaves type off (no chip); the inspector's disabled Type control still reads 'Script — run an expression / sandboxed JS' on update actions by objectui#7551's design (polish)",
"security carrier (dispatch Zone 2 #4, read only, not filed; in the PR's Acceptance notes): objectstack main c6fe02d7 — packages/objectql/src/hook-binder.ts bindHooksToEngine registers every authored hook on each object normalizeObjects(hook.object) names, '' included (only an empty target refused); packages/objectql/src/plugin.ts ObjectQLPlugin.readAuthoredHookRows/resyncAuthoredHooksNow read Studio-authored hook rows from sys_metadata across all organizations and bind them under package 'metadata-service'; the only platform-object refusal found is packages/spec/src/data/hook.zod.ts refuseBodyOnStoredMetadataTarget + packages/runtime/src/sandbox/body-runner.ts hookBodyRunnerFactory (storedMetadataBodyHookBindingRefusal) for a body-carrying hook naming sys_metadata/sys_metadata_history; nothing else found refuses sys_, isSystem or another package's objects (hook-wrappers.ts: zero hits for organization/tenant/_packageId/isSystem, control hit in hook-run-as.ts)"
],
"gates": [
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/studio-design/ (lock)",
"exit": 0,
"verdict": "Test Files 122 passed (122) · Tests 762 passed (762) · os-verify-lock: VERDICT command-exit 0"
},
{
"command": "pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/inspectors/ + 12 grep-derived files (lock)",
"exit": 0,
"verdict": "Test Files 155 passed (155) · Tests 2394 passed | 1 skipped (2395) · os-verify-lock: VERDICT command-exit 0"
},
{
"command": "pnpm --filter @object-ui/app-shell type-check (lock)",
"exit": 0,
"verdict": "os-verify-lock: VERDICT command-exit 0 (tsc --noEmit && tsc -p tsconfig.test.json)"
},
{
"command": "pnpm --filter @object-ui/app-shell build (lock)",
"exit": 0,
"verdict": "os-verify-lock: VERDICT command-exit 0; HookTargetScope only in dist/views/metadata-admin/inspectors/HookDefaultInspector.d.ts, imported by no other .d.ts"
},
{
"command": "pnpm exec eslint (11 changed .ts/.tsx)",
"exit": 0,
"verdict": "11 files, 0 errors, 4 warnings"
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK (scanned 7924 tracked text file(s); skipped 85 binary)."
},
{
"command": "pnpm check:test-path-roots",
"exit": 0,
"verdict": "check-test-path-roots: OK"
},
{
"command": "pnpm check:changeset-claims (after commit)",
"exit": 0,
"verdict": "No pending changeset names a file this change touches."
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "No test source names a pending changeset."
},
{
"command": "pnpm check:i18n-keys",
"exit": 0,
"verdict": "Every in-scope call-site key resolves against the en pack"
},
{
"command": "pnpm check:i18n-drift",
"exit": 0,
"verdict": "Every changed designer-table en value was followed by its zh row."
},
{
"command": "pnpm check:i18n-dead-keys",
"exit": 0,
"verdict": "report-only; none of the added keys listed"
},
{
"command": "pnpm check:i18n-designer-parity",
"exit": 0,
"verdict": "Every en row has a zh row, and every shared row carries the same placeholders."
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only → exit 0"
},
{
"command": "pnpm check:action-forward-parity",
"exit": 0,
"verdict": "exit 0"
},
{
"command": "pnpm check:designer-field-key-parity",
"exit": 0,
"verdict": "designer-field-key-parity: OK"
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "11 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/11820-studio-safe-defaults.md."
},
{
"command": "node scripts/check-changeset-no-major.mjs",
"exit": 0,
"verdict": "No changeset declares amajorbump."
},
{
"command": "node scripts/check-changeset-fixed.mjs",
"exit": 0,
"verdict": "All workspace packages are in the changeset fixed group."
},
{
"command": "node scripts/check-changeset-overwrite.mjs",
"exit": 0,
"verdict": "No pre-existing changeset was modified or deleted."
},
{
"command": "pnpm check:readme-exports",
"exit": 1,
"verdict": "NOT MEASURED: 73 README imports of packages whose dist/index.d.ts is not built in this worktree (none app-shell); prerequisite not met, and app-shell's exports are unchanged"
}
],
"deviations": [
"Zone 2 #1 (reuse the objectui#11786 hold): measured the hold's predicate (objectHeldEdit, metadataError.ts) and Show-me routing (StudioDesignSurface.tsx, held by objectui#11823 step 2) off this surface; the panel holds the unsaved rule itself and reuses the hold's i18n line and input hint. Cost stated in the panel header, PR and changeset.",
"i18n.ts: besides the 5 new keys (en+zh), the panel's existing rows engine.studio.rules.celMid was reworded and engine.studio.rules.celPost removed (the caption claimed new rules default to false); the claim's line names new rows.",
"New module-internal exports HookTargetScopeContext / HookTargetScope from HookDefaultInspector.tsx — measured not reached by the built entry (dist/index.d.ts); no claim amendment needed under the dispatch's public-surface definition.",
"ObjectHooksPanel: the new-hook target default was already the current object on 5e446ab; the change there is the package-scope provider, plus a pin.",
"Tests narrowed (declared in 'tests'); type-check ran on the bytes later committed unchanged as 54f36e2.",
"Rewrote ObjectValidationsPanel.test.tsx's case that pinned the 'false' default and whenKeyPin's New-menu cases (they pinned exactly the removed branch)."
],
"files_changed": [
".changeset/11820-studio-safe-defaults.md",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.reach-11820.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectActionsPanel.tsx",
"packages/app-shell/src/views/studio-design/ObjectActionsPanel.newActionDefault-11820.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectHooksPanel.tsx",
"packages/app-shell/src/views/studio-design/ObjectHooksPanel.newHookTarget-11820.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectValidationsPanel.tsx",
"packages/app-shell/src/views/studio-design/ObjectValidationsPanel.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectValidationsPanel.whenKey.test.tsx",
"packages/app-shell/src/views/studio-design/ObjectValidationsPanel.newRuleWaits-11820.test.tsx"
],
"line_budget": "not applicable (no skills/** surface)",
"cleanup": "worktree /home/user/objectui-issue-11820 removed (node_modules first, no --force); private ref refs/os-dev-11820/os-main deleted from the objectstack checkout; no dev server or monitor started"
}
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsACCEPT —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T06:44Z. PR objectui#11918, head54f36e2.- PR shape: draft against
main, branched from5e446ab, no rebase.git merge-treeof the currentmain(a49c4bd) and this head is clean; the queue tests that merge. First lineFixes #11820, no other closing keyword.Clause-②: noat line start. The commit carries only the model-free trailer pair. Assigned toos-support-ai. - Scope: 12 files, +950/−92, on the claim's surface (
6053007704): the four panels and inspectors, designer rows ini18n.ts, their pins and the changeset (patchon@object-ui/app-shell). A module-internalHookTargetScopeContextis exported fromHookDefaultInspector.tsx; the dev measured that the built entry does not reach it. Two existing designer rows were reworded or removed, because their caption claimed new rules default tofalse. No governed path. - Diff read (the seat's own):
- Rules. New builds the rule with an empty guard and
events: ['insert', 'update']. A guarded type (script, cross-field, conditional) with no condition is held in the panel's ownunsavedlist, marked with objectui#11786's hint, and committed only whenmissingGuardclears. For a conditional, the Then rule needs its condition too. An existing rule's type switch keeps the validfalseplaceholder. - Hooks.
ObjectHooksPanelsupplies the package's objects (published and draft).HookDefaultInspectorgroups the picker into This package and Other objects (other packages' andsys_*), with a reach warning on that group and on All objects (*). A new hook's target is the current object, as it already was; that is now pinned. - Actions. A new action is
operation: 'update',patch: {}, with no sandboxed JS.
- Rules. New builds the rule with an empty guard and
- Brief readings, measured:
- Zone 2 Implement visual designer for Object UI schemas #1: the pillar's hold lives in regions this card does not hold, so the panel holds the rule itself and reuses the hold's wording. The stated cost: leaving Rules before a condition drops the rule, and Publish does not wait for it.
- The card's "no Runs on event" rule did run on insert and update (the spec's default), so its no-op came from
condition: 'false'alone. - Zone 2 Add public roadmap, VitePress documentation site, and GitHub Pages deployment #3:
update_fieldis not anActionType, and url, flow, modal, api and form are refused without a target.operation: 'update'withpatch: {}is the one declarative skeleton that parses.
- The dev's open questions:
- Q1, answered A: keep the panel hold. Moving it onto
objectHeldEditwould needStudioDesignSurface.tsxregions this card does not hold, and the loss is a rule with no condition, which nothing runs. Revisit only if authors report losing unsaved rules. - Q2, answered A: the script-switch defect in
ActionDefaultInspectorpredates this PR, and choosing a language explicitly gets through today → filed studio(actions): switching an action to "Run a script" writes a body the spec refuses until a language is picked by hand — commitOperation seeds no body, and patchBody writes no language #11921, and this PR lands.
- Q1, answered A: keep the panel hold. Moving it onto
- Security carrier (triage
6043523725): the dev read objectstackmainc6fe02d7.hook-binder.ts'sbindHooksToEngineregisters an authored hook on every object its target names,*included.ObjectQLPluginreads Studio-authored hook rows fromsys_metadataacross all organizations. The only platform-object refusal found is the stored-metadata body-hook guard (sys_metadata/sys_metadata_history). Nothing else refusessys_*or another package's objects. This is in the PR's Acceptance notes for triage to route. ⛔ This seat changes nothing there: it is the maintainer's boundary. - Ruling honoured (triage
6043523725, option A): a new rule is not saved until it has a condition and starts on Create + Update; a hook's target defaults to the current object, with other packages' and platform objects grouped behind a warning; an action defaults to a declarative type. - Reverse verification (dev report):
- Ablation through
ablation-replace.mjs(the rule written at once withcondition: 'false'): 14 red across the three rule pin files. Restored with the blob equal to HEAD. studio-design/: 122 files, 762 tests passed.metadata-admin/inspectors/plus 12 derived files: 2394 tests passed.- The type-check exited 0.
- Ablation through
- Gates: CI on
54f36e2(head_shaequals the PR head): 43 runs, 40 success, 3 skipped (Test (coverage), its shard template,dependabot), 0 failure.LintandType Checkare success. Mergeable stateclean. - Contract review: not required (
Clause-②: no).
Findings:
- Switching an action to a script writes bodies the spec refuses until a language is picked → filed studio(actions): switching an action to "Run a script" writes a body the spec refuses until a language is picked by hand — commitOperation seeds no body, and patchBody writes no language #11921
- The Runs on row offers
delete, which the spec refuses, and shows an absenteventslist as nothing checked, although the spec defaults it to insert and update → filed studio(rules): the validation rule "Runs on" checkboxes disagree with the spec's events contract — they offer delete (refused) and show an absent events list as nothing checked (the spec defaults it to insert + update) #11923 (one card for both halves) - The condition builder's "Always — no condition." text shows above a required guard, hook events carry raw camelCase labels, and a written update action shows a SCRIPT chip. All three are polish →
Acceptance notes
Landing: ready + auto-merge through the queue, now.
Generated by Claude Code
- PR shape: draft against
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T07:03Z. PR objectui#11918 merged through the queue asb4693b7at 2026-10-08T07:02Z.- The merge:
- One parent,
340bfab, an ancestor oforigin/main. - The landed tree equals
git merge-treeof that parent and the accepted head54f36e2(tree44518308on both). - 12 files, +950/−92.
- One parent,
- Content check,
340bfab→b4693b7:- The new action's
type: 'script',instudio-design/ObjectActionsPanel.tsx: 1 → 0. NEW_RULE_EVENTSinObjectValidationsPanel.tsx: 0 → 2.HookTargetScopeContextininspectors/HookDefaultInspector.tsx: 0 → 3, andpackages/app-shell/src/index.tsnamesHookTargetScope0 times (module-internal).- The landed tree equality is the control for the rest of the files.
- The new action's
- Closures: the PR's only closing keyword was
Fixes #11820, and this merge closed only objectui#11820 (07:02Z). The other closures since 06:40Z are other seats' merges.pm:dispatchedis removed. - Follow-ups:
- Filed from this card: studio(actions): switching an action to "Run a script" writes a body the spec refuses until a language is picked by hand — commitOperation seeds no body, and patchBody writes no language #11921 (the script switch writes refused bodies) and studio(rules): the validation rule "Runs on" checkboxes disagree with the spec's events contract — they offer delete (refused) and show an absent events list as nothing checked (the spec defaults it to insert + update) #11923 (the Runs on row against the spec's
events). - The security carrier, a read of how the server binds an authored hook on platform objects and across organizations, is in PR objectui#11918's Acceptance notes and the ACCEPT
6054139206, for triage to route. That boundary is the maintainer's.
- Filed from this card: studio(actions): switching an action to "Run a script" writes a body the spec refuses until a language is picked by hand — commitOperation seeds no body, and patchBody writes no language #11921 (the script switch writes refused bodies) and studio(rules): the validation rule "Runs on" checkboxes disagree with the spec's events contract — they offer delete (refused) and show an absent events list as nothing checked (the spec defaults it to insert + update) #11923 (the Runs on row against the spec's
Generated by Claude Code
- The merge:
- added a commit that references this issue
on Oct 9, 2026
Filing gate ② — a product decision only the maintainer can make.
Who acts on it: objectui triage → the Studio / app-shell owner. ⛔ Not a claim. Found in a manual browser QA pass of Studio on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.
Maintainer quick-read
Three Studio defaults lean risky or produce no-ops. Change them?
Defaults seen
sys_*tables (sys_api_key,sys_approval_token, …) with no warning about reach; events are raw camelCase (beforeInsert,afterUpdateMany,beforeFind).validation_1with conditionfalseand no Runs on event checked — a rule that never fires.Options
sys_*; new rule defaults to Create + Update and is not saved until it has a condition.Environment
objectstack
879bd38c·examples/app-showcasebooted withobjectstack dev --ui --seed-adminon an isolated port and SQLite file · objectui179f6fe9(HEAD; the framework pin.objectui-shaisa58626c8) served by the console's Vite dev server, perf numbers from avite buildof the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform adminadmin@objectos.aiunless stated.Duplicate check
Dedupe words: new action default script · hook target all objects sys tables warning · validation rule created no-op false
Filed by Claude Code (session
session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.Generated by Claude Code