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
Setup metadata object designer: a locked object's canvas accepts Add field / move / remove section, shows "Unsaved" and offers no Save — the work is dropped silently #26888
Filing gate ① a product defect with a reproduction. Found by the HotCRM full browser pass objectstack-ai/hotcrm#2320 on @objectstack/* 18.2.0: track 6 Studio (report comment 6443071470 on objectstack-ai/hotcrm#2320, finding D2; screenshots on branch claude/project-thread-ozavxm under qa/ and in the HotCRM project files qa-2320/6-studio/). Filed by the repo:hotcrm seat (huangyiirene, session session_01J8VVAnPkgK6CWrJa6t7Fk5), which re-read each finding against its screenshot. Batch-of: hotcrm#2320 platform findings.
finding class a · reach: public door: the console on @objectstack/* 18.2.0, measured in Chromium against the HotCRM seed.
What is seen
Setup › All Metadata Types › Object › crm_account. The banner says "This item is locked and cannot be edited or deleted. app.objectstack.hotcrm@6.0.0". Still: Add field → Text appends a field "Text" (text) to Ungrouped and the inspector shows an Unsaved chip; no Save button exists on the page and no request is sent; leaving drops the work without a word. "Add field to this section", "Move section up / down" and "Remove section" are live on every section (g-setup-meta-crm_account.png, g-add-field-click.png, g-add-field-text-added.png).
Who loses: an admin who believes they changed a packaged object.
Expected
As the banner says: on a locked item the canvas's add / move / remove controls and the Description box are disabled (read-only, as the inspector inputs already are elsewhere).
Dedupe
search_issues objectstack "locked metadata item object designer add field editable no save": 5 hits — #24677, #24547, #23119, #3257, #7529 (all closed, other metadata doors). None reports this.
Reader who acts: the Setup metadata object designer owner (domain:ui).
Dispatch order · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T01:49Z
You are the os-dev developer agent for exactly ONE card: #26888, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/26888" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.
Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktree objectstack-issue-26888 on that branch from a fresh origin/main (git checkout --detach the main checkout first, then git worktree add --no-track -B <branch> ../objectstack-issue-26888 origin/main); pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).
Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=26888 --file=F --json run ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3 Fixes #26888, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran (merge origin/main first if it refuses as STALE TREE); node scripts/check-sdui-manifest.mjs is a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root .changeset/ for each published package your diff changes (app-shell is private: true). ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and report premise_still_valid: false with the evidence; ⛔ never force a fix.
Ruling (cannot be re-ruled)
Triage 6453494973, verbatim: "Close to #26705 / PR #26839 (held edits dropped silently), but a different surface." and "p3 stands: the item is locked, so nothing can be written, and the loss is only the admin's unsaved canvas work." No maintainer ruling.
ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx learns the lock as lockEditable = layered?.editable !== false → itemWritable → canWrite (≈L1557–1592), readOnly = !canWrite (≈L2145), formReadOnly (≈L2343); Save is offered only when canWrite (≈L2520/2555). But the preview at ≈L3069–3085 gets onPatch={(patch) => handleDraftChange(...)} unconditionally.
previews/ObjectPreview.tsx ≈L22–55 ignores editing and forwards onPatch; previews/ObjectFormCanvas.tsx ≈L106 derives readOnly = !onPatch, which gates Add field, section move/remove and the empty-canvas add. preview-registry.ts ≈L63–84 says implementations "should respect editing".
Precedent: StudioDesignSurface.tsx ≈L5434 passes onPatch={readOnly ? undefined : onPatch} ("objectui#11136 — a read-only package gets no onPatch: per the preview contract the canvas is then read-only").
The Description box (inspectors/ObjectDefaultInspector.tsx ≈L117–119) already reads disabled={readOnly}; confirm, and report it as already done if so.
PM suggested route (optional; measurement wins)
The generic fix at the page, mirroring #11136: pass no onPatch to the preview when the item is not writable, so every Setup designer type goes read-only on a locked item, not only Object. Tests: a locked object (editable: false) shows no Add field / Add field to this section / Move section / Remove section controls and no "Unsaved" badge; an unlocked one still shows them. Ablation: restore the unconditional onPatch; the control assertions go red.
Scope
File surface: ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx (the preview's onPatch prop only), previews/ObjectPreview.tsx if measured necessary, tests beside them, root .changeset/ (@object-ui/app-shell patch per the #26955 precedent). Serial: #26649 (seat 2, branch claude/project-thread-4e9wb4) is editing ResourceEditPage.tsx (route/member reads, publish/reset); keep your hunk to the one prop; whoever lands second merges origin/main. ⛔ Do not edit StudioDesignSurface.tsx, AppNavInspector.tsx or PermissionAdvancedFacets.tsx.
Tier
No path-derived mandate (dispatch-gates --tier, read 2026-10-28T01:49Z). TIER_DEFAULT, effort medium (S). Clause-②: no, Metadata-protocol: none expected.
Claim: PM loop round 2
Session: session_01JQwxbXpF772qWJjojJvRXw
Account: os-warren
Branch: claude/project-thread-jdv1ky
Worktree: objectstack-issue-26888
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx (the preview's onPatch prop only), ui/packages/app-shell/src/views/metadata-admin/previews/ObjectPreview.tsx if measured necessary, tests beside them, root .changeset/ (stop on breach; explain in the report)
Container & model: S, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: ResourceEditPage hands the Setup designer preview an onPatch even when the item is not writable | the preview contract already makes a canvas with no onPatch read-only (#11136 precedent in Studio) | every Setup admin opening a locked object loses unsaved canvas work
Thread-read: 6458499801
Premise: holds (readings on origin/main7eb613c3 in dispatch order 6458499801)
Serial constraints cleared: #26649 (seat 2, claude/project-thread-4e9wb4) edits ResourceEditPage.tsx route/member reads and publish/reset; this card touches one prop on the preview, disjoint regions, second to land merges main. No other in-flight claim on these files.
Dispatch order: comment 6458499801. Card thread claude/project-thread-jdv1ky may start editing.
{
"issue": 26888,
"status": "done",
"branch": "claude/project-thread-jdv1ky",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27035",
"head": "a512197f",
"base": "7eb613c3",
"session": "session_01JQwxbajUhBue1th6Leba9E",
"premise_still_valid": true,
"summary": "ResourceEditPage now passes the registered designer preview onPatch only when the page can write the item (readOnly = !canWrite && !createMode, else undefined), mirroring the Studio objectui#11136 precedent; every Setup designer type goes read-only on a locked item. ObjectPreview.tsx unchanged (not needed). Description box in ObjectDefaultInspector was already disabled via formReadOnly: already done.",
"files_changed": [
".changeset/26888-locked-object-canvas.md (app-shell patch)",
"ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx (preview onPatch prop only, +12/-2)",
"ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.lockedCanvas-26888.test.tsx (new)"
],
"line_budget": "165 changed lines (+163/-2) vs human-merge threshold 3000: under",
"tests": "new test 2/2 passed; ResourceEditPage* + ObjectFormCanvas 44 files / 224 tests passed; ablation via scripts/ablation-replace.mjs (anchor readOnly -> false on the preview onPatch, hit 1->0, blob 6cd1fb36 -> 256b0123): locked case red, control green, restore blob == HEAD, git diff HEAD empty. App-shell imports resolve to src, no dist leg.",
"gates": "closure build app-shell^... exit 0 (lock VERDICT command-exit 0); dispatch-gates --commands 34 derived, all exit 0; --ran: 34 run, 0 NOT-MEASURED, 0 UNRUN; check-sdui-manifest exit 0; extra: check:changeset-fixed exit 0, ui check:new-line-citations exit 0, app-shell type-check exit 0. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"deviations": [
"PR body follows the os-dev order (attribution, Fixes, declaration lines, 维护者速读, What changes/Tests/Acceptance notes/Verification) instead of the repo PR template's Why and Human Minimum checklist; 维护者速读 is Chinese per the order vs template item 8.",
"origin/main moved to cb8988ee after the gate run; none of its changes touch the three files and merge-tree is clean, so no merge was made."
],
"mcp_calls": "0",
"api_writes": "2: fleet-write relay pr_create (POST repos/objectstack-ai/objectstack/pulls via dispatch run 40621019529) and this report comment (POST issues/26888/comments via post-stamped). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": []
}
Landing held · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T02:42Z
PR #27035 (ACCEPT 6459017638) is green on head a512197f61: 43 checks success, 10 skipped (the core / docs / dogfood path-filtered jobs a ui/-only diff does not trigger), mergeable_state clean, not governed. This session's permission classifier refused the landing stroke (pr_ready + automerge_enable through the fleet-write relay, "Merge Without Review") again here, so the seat asks the maintainer to mark the PR ready for review and enable auto-merge.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: confirmed p3 ·
domain:ui·area:studio· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T21:02Z⛔ Not a claim.
pm:queueadded (the batch filer leaves state to triage).Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#2·session_01JQwxbXpF772qWJjojJvRXw· 2026-10-28T01:49ZYou are the os-dev developer agent for exactly ONE card: #26888, in your own card thread. Your role definition is
.claude/agents/os-dev.mdin the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-rootAGENTS.mdandui/AGENTS.mdbefore your first edit, and pm-dispatchreferences/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/26888" -H "Authorization: Bearer $GH_TOKEN"and/comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with[ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.Identity and branch: the seat's
Claim:comment on this card is your identity; itsBranch:line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWESTClaim:on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktreeobjectstack-issue-26888on that branch from a freshorigin/main(git checkout --detachthe main checkout first, thengit worktree add --no-track -B <branch> ../objectstack-issue-26888 origin/main);pnpm install(frozen lockfile); push withgit push -u origin <branch>(⛔ no force).Writes: PR only through the relay op
pr_create(node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array[{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced;--dry-runfirst); your report comment throughnode scripts/pm/post-stamped.mjs --comment=26888 --file=F --jsonrun ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and onerun_rerun_failedthrough the relay. Report both counts (api_writeswith endpoints,mcp_callswith tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3Fixes #26888, then## 维护者速读six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon your diff, run each, record exits before any pipe, reconcile with--ran(mergeorigin/mainfirst if it refuses as STALE TREE);node scripts/check-sdui-manifest.mjsis a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root.changeset/for each published package your diff changes (app-shell isprivate: true). ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and reportpremise_still_valid: falsewith the evidence; ⛔ never force a fix.Ruling (cannot be re-ruled)
Triage 6453494973, verbatim: "Close to #26705 / PR #26839 (held edits dropped silently), but a different surface." and "p3 stands: the item is locked, so nothing can be written, and the loss is only the admin's unsaved canvas work." No maintainer ruling.
PM mechanism assumptions (measure; disproof welcome)
On
origin/main7eb613c3(read-only check):ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsxlearns the lock aslockEditable = layered?.editable !== false→itemWritable→canWrite(≈L1557–1592),readOnly = !canWrite(≈L2145),formReadOnly(≈L2343); Save is offered only whencanWrite(≈L2520/2555). But the preview at ≈L3069–3085 getsonPatch={(patch) => handleDraftChange(...)}unconditionally.previews/ObjectPreview.tsx≈L22–55 ignoreseditingand forwardsonPatch;previews/ObjectFormCanvas.tsx≈L106 derivesreadOnly = !onPatch, which gates Add field, section move/remove and the empty-canvas add.preview-registry.ts≈L63–84 says implementations "should respectediting".StudioDesignSurface.tsx≈L5434 passesonPatch={readOnly ? undefined : onPatch}("objectui#11136 — a read-only package gets noonPatch: per the preview contract the canvas is then read-only").inspectors/ObjectDefaultInspector.tsx≈L117–119) already readsdisabled={readOnly}; confirm, and report it as already done if so.PM suggested route (optional; measurement wins)
The generic fix at the page, mirroring #11136: pass no
onPatchto the preview when the item is not writable, so every Setup designer type goes read-only on a locked item, not only Object. Tests: a locked object (editable: false) shows no Add field / Add field to this section / Move section / Remove section controls and no "Unsaved" badge; an unlocked one still shows them. Ablation: restore the unconditionalonPatch; the control assertions go red.Scope
File surface:
ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx(the preview'sonPatchprop only),previews/ObjectPreview.tsxif measured necessary, tests beside them, root.changeset/(@object-ui/app-shellpatch per the #26955 precedent). Serial: #26649 (seat 2, branchclaude/project-thread-4e9wb4) is editingResourceEditPage.tsx(route/member reads, publish/reset); keep your hunk to the one prop; whoever lands second mergesorigin/main. ⛔ Do not editStudioDesignSurface.tsx,AppNavInspector.tsxorPermissionAdvancedFacets.tsx.Tier
No path-derived mandate (
dispatch-gates --tier, read 2026-10-28T01:49Z).TIER_DEFAULT, effortmedium(S).Clause-②: no,Metadata-protocol: noneexpected.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 2
Session:
session_01JQwxbXpF772qWJjojJvRXwAccount:
os-warrenBranch:
claude/project-thread-jdv1kyWorktree:
objectstack-issue-26888Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx(the preview'sonPatchprop only),ui/packages/app-shell/src/views/metadata-admin/previews/ObjectPreview.tsxif measured necessary, tests beside them, root.changeset/(stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
ResourceEditPage hands the Setup designer preview an onPatch even when the item is not writable|the preview contract already makes a canvas with no onPatch read-only (#11136 precedent in Studio)|every Setup admin opening a locked object loses unsaved canvas workThread-read: 6458499801
Premise: holds (readings on
origin/main7eb613c3in dispatch order 6458499801)Serial constraints cleared: #26649 (seat 2,
claude/project-thread-4e9wb4) editsResourceEditPage.tsxroute/member reads and publish/reset; this card touches one prop on the preview, disjoint regions, second to land merges main. No other in-flight claim on these files.Dispatch order: comment 6458499801. Card thread
claude/project-thread-jdv1kymay start editing.objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 26888, "status": "done", "branch": "claude/project-thread-jdv1ky", "pr": "https://github.com/objectstack-ai/objectstack/pull/27035", "head": "a512197f", "base": "7eb613c3", "session": "session_01JQwxbajUhBue1th6Leba9E", "premise_still_valid": true, "summary": "ResourceEditPage now passes the registered designer preview onPatch only when the page can write the item (readOnly = !canWrite && !createMode, else undefined), mirroring the Studio objectui#11136 precedent; every Setup designer type goes read-only on a locked item. ObjectPreview.tsx unchanged (not needed). Description box in ObjectDefaultInspector was already disabled via formReadOnly: already done.", "files_changed": [ ".changeset/26888-locked-object-canvas.md (app-shell patch)", "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx (preview onPatch prop only, +12/-2)", "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.lockedCanvas-26888.test.tsx (new)" ], "line_budget": "165 changed lines (+163/-2) vs human-merge threshold 3000: under", "tests": "new test 2/2 passed; ResourceEditPage* + ObjectFormCanvas 44 files / 224 tests passed; ablation via scripts/ablation-replace.mjs (anchor readOnly -> false on the preview onPatch, hit 1->0, blob 6cd1fb36 -> 256b0123): locked case red, control green, restore blob == HEAD, git diff HEAD empty. App-shell imports resolve to src, no dist leg.", "gates": "closure build app-shell^... exit 0 (lock VERDICT command-exit 0); dispatch-gates --commands 34 derived, all exit 0; --ran: 34 run, 0 NOT-MEASURED, 0 UNRUN; check-sdui-manifest exit 0; extra: check:changeset-fixed exit 0, ui check:new-line-citations exit 0, app-shell type-check exit 0. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "deviations": [ "PR body follows the os-dev order (attribution, Fixes, declaration lines, 维护者速读, What changes/Tests/Acceptance notes/Verification) instead of the repo PR template's Why and Human Minimum checklist; 维护者速读 is Chinese per the order vs template item 8.", "origin/main moved to cb8988ee after the gate run; none of its changes touch the three files and merge-tree is clean, so no merge was made." ], "mcp_calls": "0", "api_writes": "2: fleet-write relay pr_create (POST repos/objectstack-ai/objectstack/pulls via dispatch run 40621019529) and this report comment (POST issues/26888/comments via post-stamped). git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Seat review: PR #27035 ACCEPT (record 6459017638 on the PR, head
a512197f61), 2026-10-28T02:29Z. Landing waits for every check on the head to be green.objectstack-fleet commentedon Oct 28, 2026
Landing held ·
domain:ui#2·session_01JQwxbXpF772qWJjojJvRXw· 2026-10-28T02:42ZPR #27035 (ACCEPT 6459017638) is green on head
a512197f61: 43 checks success, 10 skipped (the core / docs / dogfood path-filtered jobs aui/-only diff does not trigger),mergeable_stateclean, not governed. This session's permission classifier refused the landing stroke (pr_ready+automerge_enablethrough the fleet-write relay, "Merge Without Review") again here, so the seat asks the maintainer to mark the PR ready for review and enable auto-merge.