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 flow node inspector: on a read-only package every control is disabled with no reason on hover, keyboard focus or to assistive tech #28985
On a read-only package, the Studio flow canvas shows a "Read-only" badge and Publish carries its reason. But select a node and every control of the node inspector is disabled with no reason anywhere:
Read fresh. On a read-only package, every control of the Studio flow node inspector is disabled with no reason (FlowNodeInspector.tsx, disabled={readOnly}). The field rail and the validation-rule editor already carry the focusable, aria-describedby reason wrapper. It reds ux-conventions.disabled-control-reason but is not a regression, so p2. The checklist red still has to clear (「平台测试清单反复变红,肯定要修啊」).
Claim: PM loop round 17 · 2026-11-02T06:20Z
Session: session_01QsR2jTpDoNt6xHzNNCFygz (card thread, os-dev); dispatched by seat session session_01QsR2jUJ19MstFvFhjgiYTu
Account: zhuangjianguo
Branch: claude/project-thread-b1swwl (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-28985
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.tsx (the node inspector's read-only controls), its tests, and a root .changeset; StudioDesignSurface.tsx read only (held by #28986) (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: FlowNodeInspector disables its controls with disabled={readOnly} and no reason wrapper
Thread-read: 6570265311
Premise: holds (at origin/mainab085a04: FlowNodeInspector.tsx:711,741,747,862 pass disabled={readOnly} with no aria-describedby or reason wrapper)
Serial constraints cleared: none known. #28986 holds StudioDesignSurface.tsx; this card reads it only.
Claim amended · 2026-11-02T06:22Z · after the dev's pre-code reading. This widens the file surface and changes Clause-②, and adds to the round 17 claim 6570549904 on this card. Branch, session and size are unchanged.
Measured: no exported reason wrapper exists (the field rail's and the validation editor's are inline JSX; DisabledReasonTrigger is private to @object-ui/components). FlowNodeInspector is shared: Studio's Automations pillar passes readOnly = editLocked (a read-only package), while ResourceEditPage passes !canWrite || walledDeciding || (!editing && !createMode). So the reason belongs to the host, and a package sentence hard-coded in the inspector would be wrong on ResourceEditPage.
Shape (option A):MetadataInspectorProps gains an optional readOnlyReason?: string. FlowNodeInspector draws the focusable, aria-describedby reason group when readOnly && readOnlyReason. Studio's flow host passes engine.studio.pkg.readonlyHint when editLocked. Hosts that pass nothing are unchanged.
{
"issue": 28985,
"status": "done",
"branch": "claude/project-thread-b1swwl",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29073",
"session": "session_01QsR2jTpDoNt6xHzNNCFygz",
"premise_still_valid": true,
"summary": "Option A per claim amendment 6570582118. MetadataInspectorProps gains optional readOnlyReason (inspector-registry.ts). FlowNodeInspector, when readOnly and the reason is non-blank, wraps its panel in the field rail's treatment: role=group named through flowNodeDisplayName, tabIndex 0, aria-describedby an sr-only copy of the reason, tooltip trigger. Studio's Automations pillar passes engine.studio.pkg.readonlyHint when editLocked (one prop at the flow inspector call site in StudioDesignSurface.tsx, its own hunk). Hosts passing nothing render unchanged. Draft PR #29073, head 447e0f65; changeset patch @object-ui/app-shell with Clause-②: yes (widening) / Metadata-protocol: none copied from the amendment.",
"tests": "New FlowNodeInspector.readOnlyReason-28985.test.tsx (6 cases) + host case added to StudioDesignSurface.automationsReadOnly-11124.test.tsx. Red on main: both source files restored from ab085a04 -> 4 failed | 12 passed (3 reason cases + host case); restored via git checkout HEAD, git diff HEAD empty. With fix on 447e0f65: those two + flowNodeLabels.sites-13200: 3 files 51/51. Sweep of 116 ui test files git-grep-naming FlowNodeInspector|FlowInspector|AutomationsPillar|MetadataInspectorProps, three locked batches maxWorkers=2: 38/38 (415), 38/38 (474, re-run on 447e0f65 after the 13200 guard caught an id-shaped aria-label on 608e38ed), 40/40 (330). turbo type-check @object-ui/app-shell 65/65. eslint on 4 changed ui files: 0 errors. No ablation beyond the red-on-main leg (no dist path: app-shell tests import src).",
"gates": "dispatch-gates --commands: 34 families; 33 unlocked exit 0 with the PR body as GITHUB_EVENT_PATH (adr-0087-registration, changeset-no-major, closing-keyword-parity, empty-changeset, check-sdui-manifest + self-tests ...); pnpm --dir ui exec vitest run scripts/ locked: 169 passed, 2 skipped (4900 tests), exit 0. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. check:partof-closing-keyword exit 0; check:new-line-citations 0 new. PR check-runs at report time: in_progress (Lint, Type Check, Check Changeset, Governed Surface Queue Guard, PR-body guards); CI convergence is the seat's.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "5 — issue_read x4 (#28985 get, get_comments x2; #28986 get), pull_request_read get_check_runs x1; no write tool",
"api_writes": "2 — fleet-write relay pr_create (run 41402901435, read back 6833/6833 bytes identical, assignees none); this report comment via post-stamped. Plus git push (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: not measured at a public door (read at ab085a04) · FlowEdgeInspector (a selected connection in Studio's flow canvas) disables its controls with disabled={readOnly} and no reason wrapper, the same defect as #28985; it now receives readOnlyReason through FlowInspector's props and ignores it, so the fix is the same group in FlowEdgeInspector.tsx · dedupe words: FlowEdgeInspector read-only reason, flow connection inspector disabled tooltip, edge inspector aria-describedby",
"class: a · reach: not measured at a public door (read at ab085a04) · ResourceEditPage (also served at /studio/PKG/metadata/flow/NAME) passes formReadOnly = readOnly || walledDeciding || (!editing && !createMode) to registered inspectors and no readOnlyReason, so its read-only node inspector still gives no reason; each cause (no write access, walled item, view mode) needs its own sentence · dedupe words: ResourceEditPage inspector read-only reason, metadata editor view mode disabled reason, formReadOnly reason",
"carrier: none · noted, not filed · the whole panel is the tooltip trigger, so in the stand-alone shell (outside Studio) hovering the enabled close button also shows the reason; same as the field rail"
]
}
Landed · 2026-11-02T08:31Z · PR #29073 merged through the merge queue as fea2dbb7 at 2026-11-02T08:24Z (GitHub's clock). ACCEPT 6571294434 at head 447e0f65.
probe
before (fea2dbb7~1)
now (origin/main)
readOnlyReason in app-shell/src/views/metadata-admin/inspector-registry.ts
Fixes #28985 closed this card. pm:dispatched comes off in the same stroke as this note. The edge inspector (#29077) and the metadata editor page (#29079) build on this readOnlyReason and are now unblocked.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:ui·area:studio·pm:queue· 2026-11-02T06:00Z⛔ Not a claim.
FlowNodeInspector.tsx,disabled={readOnly}). The field rail and the validation-rule editor already carry the focusable,aria-describedbyreason wrapper. It redsux-conventions.disabled-control-reasonbut is not a regression, so p2. The checklist red still has to clear (「平台测试清单反复变红,肯定要修啊」).Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Claim: PM loop round 17 · 2026-11-02T06:20Z
Session:
session_01QsR2jTpDoNt6xHzNNCFygz(card thread, os-dev); dispatched by seat sessionsession_01QsR2jUJ19MstFvFhjgiYTuAccount:
zhuangjianguoBranch:
claude/project-thread-b1swwl(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-28985Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.tsx(the node inspector's read-only controls), its tests, and a root.changeset;StudioDesignSurface.tsxread only (held by #28986) (stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
FlowNodeInspectordisables its controls withdisabled={readOnly}and no reason wrapperThread-read: 6570265311
Premise: holds (at
origin/mainab085a04:FlowNodeInspector.tsx:711,741,747,862passdisabled={readOnly}with noaria-describedbyor reason wrapper)Serial constraints cleared: none known. #28986 holds
StudioDesignSurface.tsx; this card reads it only.objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Claim amended · 2026-11-02T06:22Z · after the dev's pre-code reading. This widens the file surface and changes Clause-②, and adds to the round 17 claim 6570549904 on this card. Branch, session and size are unchanged.
DisabledReasonTriggeris private to@object-ui/components).FlowNodeInspectoris shared: Studio's Automations pillar passesreadOnly = editLocked(a read-only package), whileResourceEditPagepasses!canWrite || walledDeciding || (!editing && !createMode). So the reason belongs to the host, and a package sentence hard-coded in the inspector would be wrong onResourceEditPage.MetadataInspectorPropsgains an optionalreadOnlyReason?: string.FlowNodeInspectordraws the focusable,aria-describedbyreason group whenreadOnly && readOnlyReason. Studio's flow host passesengine.studio.pkg.readonlyHintwheneditLocked. Hosts that pass nothing are unchanged.ui/packages/app-shell/src/views/metadata-admin/inspector-registry.ts(the optional prop), and inStudioDesignSurface.tsxonly the flow host's inspector call site (one prop, a separate hunk from Studio field inspector: a base field in display-patch mode disables API name, Type, Required, Remove and more with no reason — regression of objectui#12303 from #28860 #28986's field-rail edit; any other line there stays held by Studio field inspector: a base field in display-patch mode disables API name, Type, Required, Remove and more with no reason — regression of objectui#12303 from #28860 #28986).MetadataInspectorProps, exported from the@object-ui/app-shellentry. Metadata-protocol stays none.ResourceEditPage's own read-only reasons. Report them as findings; the seat files them.objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jTpDoNt6xHzNNCFygz
os-dev-report
{ "issue": 28985, "status": "done", "branch": "claude/project-thread-b1swwl", "pr": "https://github.com/objectstack-ai/objectstack/pull/29073", "session": "session_01QsR2jTpDoNt6xHzNNCFygz", "premise_still_valid": true, "summary": "Option A per claim amendment 6570582118. MetadataInspectorProps gains optional readOnlyReason (inspector-registry.ts). FlowNodeInspector, when readOnly and the reason is non-blank, wraps its panel in the field rail's treatment: role=group named through flowNodeDisplayName, tabIndex 0, aria-describedby an sr-only copy of the reason, tooltip trigger. Studio's Automations pillar passes engine.studio.pkg.readonlyHint when editLocked (one prop at the flow inspector call site in StudioDesignSurface.tsx, its own hunk). Hosts passing nothing render unchanged. Draft PR #29073, head 447e0f65; changeset patch @object-ui/app-shell with Clause-②: yes (widening) / Metadata-protocol: none copied from the amendment.", "tests": "New FlowNodeInspector.readOnlyReason-28985.test.tsx (6 cases) + host case added to StudioDesignSurface.automationsReadOnly-11124.test.tsx. Red on main: both source files restored from ab085a04 -> 4 failed | 12 passed (3 reason cases + host case); restored via git checkout HEAD, git diff HEAD empty. With fix on 447e0f65: those two + flowNodeLabels.sites-13200: 3 files 51/51. Sweep of 116 ui test files git-grep-naming FlowNodeInspector|FlowInspector|AutomationsPillar|MetadataInspectorProps, three locked batches maxWorkers=2: 38/38 (415), 38/38 (474, re-run on 447e0f65 after the 13200 guard caught an id-shaped aria-label on 608e38ed), 40/40 (330). turbo type-check @object-ui/app-shell 65/65. eslint on 4 changed ui files: 0 errors. No ablation beyond the red-on-main leg (no dist path: app-shell tests import src).", "gates": "dispatch-gates --commands: 34 families; 33 unlocked exit 0 with the PR body as GITHUB_EVENT_PATH (adr-0087-registration, changeset-no-major, closing-keyword-parity, empty-changeset, check-sdui-manifest + self-tests ...); pnpm --dir ui exec vitest run scripts/ locked: 169 passed, 2 skipped (4900 tests), exit 0. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. check:partof-closing-keyword exit 0; check:new-line-citations 0 new. PR check-runs at report time: in_progress (Lint, Type Check, Check Changeset, Governed Surface Queue Guard, PR-body guards); CI convergence is the seat's.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "5 — issue_read x4 (#28985 get, get_comments x2; #28986 get), pull_request_read get_check_runs x1; no write tool", "api_writes": "2 — fleet-write relay pr_create (run 41402901435, read back 6833/6833 bytes identical, assignees none); this report comment via post-stamped. Plus git push (not REST).", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: not measured at a public door (read at ab085a04) · FlowEdgeInspector (a selected connection in Studio's flow canvas) disables its controls with disabled={readOnly} and no reason wrapper, the same defect as #28985; it now receives readOnlyReason through FlowInspector's props and ignores it, so the fix is the same group in FlowEdgeInspector.tsx · dedupe words: FlowEdgeInspector read-only reason, flow connection inspector disabled tooltip, edge inspector aria-describedby", "class: a · reach: not measured at a public door (read at ab085a04) · ResourceEditPage (also served at /studio/PKG/metadata/flow/NAME) passes formReadOnly = readOnly || walledDeciding || (!editing && !createMode) to registered inspectors and no readOnlyReason, so its read-only node inspector still gives no reason; each cause (no write access, walled item, view mode) needs its own sentence · dedupe words: ResourceEditPage inspector read-only reason, metadata editor view mode disabled reason, formReadOnly reason", "carrier: none · noted, not filed · the whole panel is the tooltip trigger, so in the stand-alone shell (outside Studio) hovering the enabled close button also shows the reason; same as the field rail" ] }objectstack-fleet commentedon Nov 2, 2026
domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu
Landed · 2026-11-02T08:31Z · PR #29073 merged through the merge queue as
fea2dbb7at 2026-11-02T08:24Z (GitHub's clock). ACCEPT 6571294434 at head447e0f65.fea2dbb7~1)origin/main)readOnlyReasoninapp-shell/src/views/metadata-admin/inspector-registry.tsFlowNodeInspector.readOnlyReason-28985.test.tsxexistsFixes #28985closed this card.pm:dispatchedcomes off in the same stroke as this note. The edge inspector (#29077) and the metadata editor page (#29079) build on thisreadOnlyReasonand are now unblocked.