You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Studio: add a new action to an installed app's object, saved in the customization package #30088
The maintainer: 「studio没有操作类型的元数据吗?除了批量操作,别的也需要配置的呀。」 (2026-11-04). Measured by the seat on main63b86b385d, on the example CRM and a throwaway writable package. No domain:* label is set: triage routes this card.
Business reason
An operations person wants a 标记合格 button on the leads of an installed CRM. Studio has a good action editor, but only for a writable package. On an installed app the object's 操作 tab lists the vendor's actions and offers no 新建, so the most common real case is the one case with no door. The platform already accepts the result.
Measured
Check
Result
Writable package, 对象 → 高级 → 操作
新建操作 with presets (修改下拉选择字段, 运行流程, 打开网页, 在对话框中打开页面, 高级), the full editor and placements 记录头部, 记录 · 更多菜单, 记录 · 分区, 记录 · 关联列表, 列表 · 行
Read-only CRM, same tab
Lists the actions, with no 新建 or 删除, and the 只读 chip shows
PUT /api/v1/meta/action/eval_1104_lead_qualify?package=com.example.crm.custom&mode=draft, a new name with objectName: 'crm_lead', then publish
200, published
The running CRM
The row menu shows the new action, and it ran: POST /api/v1/actions/crm_lead/…. The move it tried was not allowed, and the state machine refused it with 线索不能变更到这个状态。
The Studio tab after that publish
Lists it, with type SCRIPT and no 新增 mark
The action in a writable package is stored on the object draft (draft.actions, StudioDesignSurface.tsxCelPanelTab), not as a separate item. On a read-only object that route cannot save, so the door has to write a standalone action item into the pair, as the API call above did.
Acceptance
On a read-only base with a pair, the object's 操作 tab offers 新建. It uses the same presets and editor and saves a new-named action into the pair as a draft.
Vendor actions stay read-only in that list, and the added one carries the customization mark and can be edited and removed.
It publishes with 待发布的变更, and 仅看定制 counts it.
A browser check on the CRM: add, publish, see it in the row menu, run it, remove it.
No spec or server change is expected. If one is needed, report it here.
Not in this card
Changing a vendor's action (#30065), and selection-bar placement (the decision card filed beside this one).
Claim: PM loop round 1
Session: session_01WGufzemmgi8p8uY913FXHx (card thread executor; seat session session_01WGufzinedUGSXteN45poyo)
Account: os-support-ai
Branch: claude/project-thread-yom13c (harness-pinned card thread branch; the claude/issue-<n>-<slug> convention does not apply to a project card thread)
Worktree: objectstack-issue-30088
Domain: domain:ui
Seat: domain:ui#4
File surface: ui/packages/app-shell/src/views/studio-design/** (ObjectActionsPanel.tsx, StudioDesignSurface.tsx actions branch, pair/customization helpers), its tests, root .changeset/
Container & model: M–L, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; PM judgment call, default tier), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6632526505
Premise: holds
Serial constraints cleared: #30063 (domain:ui#1, page customize door, may touch StudioDesignSurface.tsx / StudioCustomizeControls.tsx) and #30064 (domain:ui#3, dataset door) share studio-design files: ordinary concurrency, merge main before PR and after a sibling lands. #30089 (ruled A) and #30065 (domain:server) are triage-serial on the action editor and stay unclaimed by this seat until this card lands.
{
"issue": 30088,
"status": "done",
"branch": "claude/project-thread-yom13c",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30211",
"head": "238e15e3",
"session": "session_01WGufzemmgi8p8uY913FXHx",
"premise_still_valid": true,
"summary": "Acceptance 1-4 ship in one PR. On a read-only base with a pair (or one the ensure door can create), the Data pillar Actions view offers New with the same presets and editor; the action is written as a standalone action item into the pair as a draft (objectName bound, ifNoneMatch star, ensure door asked on first write). Vendor rows stay read-only; added rows carry the added CustomizationMark, autosave into the pair draft, and Delete resets the pair draft then the published row. The Actions list has its own only-customizations toggle counting added actions. No spec or server change (Acceptance 5 holds). The object rail count does not include pair actions because the served customization read lists none; noted in Acceptance notes.",
"tests": "New DataPillar.pairActions-30088.test.tsx 5/5 passed (incl. CONTROL on a writable package). Ablations via scripts/ablation-replace.mjs, each restored with git diff HEAD empty: pair route off -> 3 failed/2 passed; door gate off -> 1 failed/4 passed; underscore-key strip off -> 1 failed/4 passed. Sweep on head 238e15e3: 79 app-shell files importing DataPillar/ObjectActionsPanel/pairObjectActions + 95 i18n files = 174 files, 1977 passed, 13 skipped (narrowed from 214 StudioDesignSurface importers; only the Data pillar Actions branch changed there). Browser check on app-crm dev boot with 12 screenshots in ui-orders/30088-shots/: add (ensure 201, PUT draft into pair 200), publish (publishedCount 1), row menu shows the action, run (POST /actions/crm_lead/crm_lead_action_2 200, row turned qualified), remove (DELETE at pair 200, gone from Studio and the row menu).",
"gates": "Local, head 238e15e3: full pnpm build exit 0 then turbo build app-shell closure exit 0; type-check app-shell + i18n exit 0; npx eslint on 4 touched files 0 errors; dispatch-gates --commands 35 commands all exit 0 (incl. check-sdui-manifest.mjs, vitest run scripts/, check:type-check-debt); --ran: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs: in_progress at report time.",
"line_budget": "+908 / -30, 7 files, about 940 changed lines; under the 1,500 split line and 3,000 cap",
"files_changed": [
".changeset/30088-studio-pair-object-actions.md",
"ui/packages/app-shell/src/views/studio-design/DataPillar.pairActions-30088.test.tsx",
"ui/packages/app-shell/src/views/studio-design/ObjectActionsPanel.tsx",
"ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"ui/packages/app-shell/src/views/studio-design/pairObjectActions.ts",
"ui/packages/i18n/src/namespaces/studio/en.ts",
"ui/packages/i18n/src/namespaces/studio/zh.ts"
],
"deviations": [
"File surface: the claim names ui/packages/app-shell/src/views/studio-design/** and the changeset; the diff also adds one key (engine.studio.actions.renameHeld) to ui/packages/i18n/src/namespaces/studio/en.ts and zh.ts, and the changeset names @object-ui/i18n. Seat may append that to the claim's file surface.",
"The harness asked to set an assignee and reviewer on the PR via MCP; skipped per the order (no assignee, no reviewer, no MCP writes).",
"Browser check ran on f8bdead2 (before merging main 474c030b); the merge touched no file this diff changes."
],
"dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 MCP GitHub calls (reads went through gh api REST search and issue reads)",
"api_writes": "2: relay pr_create (PR 30211, read back identical, 9887 bytes) and this os-dev-report comment via post-stamped.mjs; plus 4 git pushes",
"open_questions": [
{
"question": "Should the object rail (not only the Actions list) mark and count a base object that carries a pair action?",
"options": [
"A: leave it on the Actions list (this PR)",
"B: extend the served customization read with pair actions on base objects (spec + server card)"
],
"recommendation": "A, because B is a spec and server change outside Acceptance 5; file B only if the maintainer wants the object row marked."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed: the object rail only-customizations count reads the served customization read, which lists no standalone pair action (PR Acceptance notes)"
]
}
The seat's landing stroke node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F with F = [{"op":"pr_ready","pull":30211},{"op":"automerge_enable","pull":30211}] was refused by the seat session's permission classifier before anything was sent (no explanation given). Nothing was written.
Activity
objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 kept ·
domain:ui· 2026-11-04T13:57Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:ui#4 · os-support-ai · session_01WGufzinedUGSXteN45poyo
Claim: PM loop round 1
Session:
session_01WGufzemmgi8p8uY913FXHx(card thread executor; seat sessionsession_01WGufzinedUGSXteN45poyo)Account:
os-support-aiBranch:
claude/project-thread-yom13c(harness-pinned card thread branch; theclaude/issue-<n>-<slug>convention does not apply to a project card thread)Worktree:
objectstack-issue-30088Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/app-shell/src/views/studio-design/**(ObjectActionsPanel.tsx, StudioDesignSurface.tsx actions branch, pair/customization helpers), its tests, root.changeset/Container & model:
M–L,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; PM judgment call, default tier),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6632526505
Premise: holds
Serial constraints cleared:
#30063(domain:ui#1, page customize door, may touch StudioDesignSurface.tsx / StudioCustomizeControls.tsx) and#30064(domain:ui#3, dataset door) share studio-design files: ordinary concurrency, merge main before PR and after a sibling lands.#30089(ruled A) and#30065(domain:server) are triage-serial on the action editor and stay unclaimed by this seat until this card lands.objectstack-fleet commentedon Nov 5, 2026
domain:ui#4 · os-support-ai · session_01WGufzemmgi8p8uY913FXHx
os-dev-report
Stamp: 2026-11-05T02:07Z
{ "issue": 30088, "status": "done", "branch": "claude/project-thread-yom13c", "pr": "https://github.com/objectstack-ai/objectstack/pull/30211", "head": "238e15e3", "session": "session_01WGufzemmgi8p8uY913FXHx", "premise_still_valid": true, "summary": "Acceptance 1-4 ship in one PR. On a read-only base with a pair (or one the ensure door can create), the Data pillar Actions view offers New with the same presets and editor; the action is written as a standalone action item into the pair as a draft (objectName bound, ifNoneMatch star, ensure door asked on first write). Vendor rows stay read-only; added rows carry the added CustomizationMark, autosave into the pair draft, and Delete resets the pair draft then the published row. The Actions list has its own only-customizations toggle counting added actions. No spec or server change (Acceptance 5 holds). The object rail count does not include pair actions because the served customization read lists none; noted in Acceptance notes.", "tests": "New DataPillar.pairActions-30088.test.tsx 5/5 passed (incl. CONTROL on a writable package). Ablations via scripts/ablation-replace.mjs, each restored with git diff HEAD empty: pair route off -> 3 failed/2 passed; door gate off -> 1 failed/4 passed; underscore-key strip off -> 1 failed/4 passed. Sweep on head 238e15e3: 79 app-shell files importing DataPillar/ObjectActionsPanel/pairObjectActions + 95 i18n files = 174 files, 1977 passed, 13 skipped (narrowed from 214 StudioDesignSurface importers; only the Data pillar Actions branch changed there). Browser check on app-crm dev boot with 12 screenshots in ui-orders/30088-shots/: add (ensure 201, PUT draft into pair 200), publish (publishedCount 1), row menu shows the action, run (POST /actions/crm_lead/crm_lead_action_2 200, row turned qualified), remove (DELETE at pair 200, gone from Studio and the row menu).", "gates": "Local, head 238e15e3: full pnpm build exit 0 then turbo build app-shell closure exit 0; type-check app-shell + i18n exit 0; npx eslint on 4 touched files 0 errors; dispatch-gates --commands 35 commands all exit 0 (incl. check-sdui-manifest.mjs, vitest run scripts/, check:type-check-debt); --ran: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs: in_progress at report time.", "line_budget": "+908 / -30, 7 files, about 940 changed lines; under the 1,500 split line and 3,000 cap", "files_changed": [ ".changeset/30088-studio-pair-object-actions.md", "ui/packages/app-shell/src/views/studio-design/DataPillar.pairActions-30088.test.tsx", "ui/packages/app-shell/src/views/studio-design/ObjectActionsPanel.tsx", "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx", "ui/packages/app-shell/src/views/studio-design/pairObjectActions.ts", "ui/packages/i18n/src/namespaces/studio/en.ts", "ui/packages/i18n/src/namespaces/studio/zh.ts" ], "deviations": [ "File surface: the claim names ui/packages/app-shell/src/views/studio-design/** and the changeset; the diff also adds one key (engine.studio.actions.renameHeld) to ui/packages/i18n/src/namespaces/studio/en.ts and zh.ts, and the changeset names @object-ui/i18n. Seat may append that to the claim's file surface.", "The harness asked to set an assignee and reviewer on the PR via MCP; skipped per the order (no assignee, no reviewer, no MCP writes).", "Browser check ran on f8bdead2 (before merging main 474c030b); the merge touched no file this diff changes." ], "dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 MCP GitHub calls (reads went through gh api REST search and issue reads)", "api_writes": "2: relay pr_create (PR 30211, read back identical, 9887 bytes) and this os-dev-report comment via post-stamped.mjs; plus 4 git pushes", "open_questions": [ { "question": "Should the object rail (not only the Actions list) mark and count a base object that carries a pair action?", "options": [ "A: leave it on the Actions list (this PR)", "B: extend the served customization read with pair actions on base objects (spec + server card)" ], "recommendation": "A, because B is a spec and server change outside Acceptance 5; file B only if the maintainer wants the object row marked." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed: the object rail only-customizations count reads the served customization read, which lists no standalone pair action (PR Acceptance notes)" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:ui#4 · os-support-ai · session_01WGufzinedUGSXteN45poyo
Landing stroke refused · PR #30211 · 2026-11-05T02:24Z
238e15e392dea312977b06e0880eac4ef105d261: 55 check-runs, 44 success + 11 skipped, 0 red;mergeable_stateclean; seat ACCEPT 6647699932 on this head; NOT governed (938 lines).node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file Fwith F =[{"op":"pr_ready","pull":30211},{"op":"automerge_enable","pull":30211}]was refused by the seat session's permission classifier before anything was sent (no explanation given). Nothing was written.