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 page block inspector: InspectorJsonField's Label is not associated with its textarea, so curated JSON props have no accessible name #27178
In ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx, InspectorJsonField renders a Label as a sibling of its textarea with no htmlFor / id pairing. Every curated JSON prop of a page block (for example record:related_list's add, and the sort / columns JSON fallback for a stored union arm) therefore exposes a textarea with no accessible name. testing-library reports it as Found a label with the text of: Sort, however no form control was found associated to that label.
Reach
Public door: Studio → page designer → block inspector, any block prop drawn by InspectorJsonField. Screen readers announce an unnamed text box; getByLabelText cannot target the field in tests.
Expected
Label htmlFor ↔ textarea id (or aria-labelledby), the same pairing the inspector's other rows use; one pin that queries the field by its label.
Dedupe words
InspectorJsonField label htmlFor textarea accessible name PageBlockInspector
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from PR #27163's out-of-scope findings (class a, reach measured), under epic #26676.
Read fresh. InspectorJsonField's Label has no htmlFor / id pairing, so curated JSON props have no accessible name. It is a11y with no wrong data, so p3.
Dispatch order · domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T10:00Z
You are the os-dev developer agent for exactly ONE card: #27178, 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/NNNNN" -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 "Dispatch the ui lane" one message with your branch name and wait. Worktree objectstack-issue-27178 on that branch from a fresh 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=27178 --file=F --json run ONCE with output captured to a file (timestamps only as {{NOW}} / {{WAS:…}}). ⛔ 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, 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. Changeset in the root .changeset/ when a published package changes. ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.
Ruling (cannot be re-ruled)
Triage first grade (2026-10-28 09:56Z, triage#1): p3, domain:ui, area:studio. Verbatim: "InspectorJsonField's Label has no htmlFor / id pairing, so curated JSON props have no accessible name. It is a11y with no wrong data, so p3." No maintainer ruling.
PM mechanism assumptions (measure on origin/main2f96d874; disproof welcome)
ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx ≈L137 InspectorJsonField renders Label beside its textarea with no pairing. Pair them the way the inspector's other rows do (find the shared row helper, e.g. InspectorTextField, and its id scheme — React.useId or the existing idPath helper); prefer reusing that helper's pairing over a new scheme.
Sweep the same inspectors directory for other Label + control pairs with no association; fix only those in PageBlockInspector.tsx, and list the rest in the report as findings.
Scope
File surface: PageBlockInspector.tsx (InspectorJsonField only), + tests. Tests: getByLabelText('Sort') (or another curated JSON prop) finds the textarea; one other inspector row unchanged (control). Ablation: drop htmlFor ⇒ the label test goes red. Stay under 300 changed lines.
Serial: seat 4's PR #27163 (#27054, draft) edits PageBlockInspector.tsx at the import block and ≈L541–700 (it adds new InspectorJsonField call sites), not the InspectorJsonField definition. Merge origin/main before your gates; if #27163 lands first, merge again and confirm its new call sites get the pairing too.
Tier
Quote dispatch-gates --tier on your surface. Expected none. TIER_DEFAULT, effort low. Clause-②: no, Metadata-protocol: none. @object-ui/app-shell is private: skip-changeset.
Dispatch order · domain:ui#5 · session_01JQwxbXGpVJHZTXXerMoY4C · 2026-10-28T11:46Z
You are the os-dev developer agent for exactly ONE card: #27178, 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/27178" -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 another ui lane seat" one message with your branch name and wait. Worktree objectstack-issue-27178 on that branch from a fresh 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=27178 --file=F --json run ONCE with output captured to a file (timestamps only as {{WAS:…}} / the now-token). ⛔ 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 #27178, 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; node scripts/check-sdui-manifest.mjs is a gate on every ui card. Changeset in the root .changeset/ when a published package changes (@object-ui/i18n patch for namespace rows; 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 "Take another ui lane seat" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.
Shared-file rules for every Studio Interfaces card this round (from ui seat 1, which holds the neighbouring work):
Supersedes ui seat 1's order 6466033575 on this card, by agreement with seat 1: seat 5 takes #27178, and seat 1 takes #27177 under its own order 6466026033. Its serial note about #27163 is moot.
Shared files: ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx was last edited by seat 4's PR #27163 (#27054, merged 2026-10-28T11:02Z). Seat 1's #27177 (the page designer canvas) runs beside this card on other files (previews/). Measure open PRs on the file at your first edit.
On origin/main2d05cd47: InspectorJsonField (PageBlockInspector.tsx ≈L170) draws <Label>{label}</Label> with no htmlFor, beside its textarea with no id. Its sibling rows in the same file (≈L244, ≈L262) already pair label and control with React.useId(). The fix is that pairing: useId() → Label htmlFor ↔ textarea id. Keep any existing aria-describedby / error wiring.
Scope
File surface: PageBlockInspector.tsx (InspectorJsonField only), a test beside it. Test (acceptance): getByLabelText finds the JSON textarea of a curated JSON prop (e.g. record:related_list's add, or a sort / columns JSON fallback) by its label, and the "Found a label … however no form control was found" warning is gone. Ablation: drop the htmlFor / id pair and the test turns red.
Tier
No path-derived mandate expected. TIER_DEFAULT, effort low (XS). Clause-②: no, Metadata-protocol: none. @object-ui/app-shell is private: true: skip-changeset.
Claim: PM loop round 10
Session: session_01JQwxbXGpVJHZTXXerMoY4C
Account: os-warren
Branch: claude/project-thread-qresy5
Worktree: objectstack-issue-27178
Domain: domain:ui
Seat: domain:ui#5
File surface: ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx (InspectorJsonField only), a test beside it (stop on breach; explain in the report)
Container & model: XS, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate expected), effort: low
Clause-②: no
Metadata-protocol: none
Responsibility: ui/packages/app-shell Studio page block inspector produces it (this lane) | a curated JSON prop's textarea has no accessible name | every page block prop drawn by InspectorJsonField
Thread-read: 6468168275
Premise: holds (InspectorJsonField ≈L170 draws <Label> with no htmlFor and a textarea with no id; siblings ≈L244/≈L262 pair with useId(); on origin/main2d05cd47; readings in dispatch order 6468168275)
Serial constraints cleared: seat 4's PR #27163 on this file merged; ui seat 1 takes #27177 (other files) and agreed this card is seat 5's; seat 1's order 6466033575 is superseded by 6468168275.
Dispatch order: comment 6468168275. Card thread claude/project-thread-qresy5 may start editing.
{
"issue": 27178,
"status": "done",
"branch": "claude/project-thread-qresy5",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27254",
"head": "e781cf8f",
"session": "session_01JQwxbW6EicqdfE9onssvrp",
"premise_still_valid": true,
"mechanism": "PM assumption confirmed on origin/main dfe85c9a: InspectorJsonField drew Label with no htmlFor beside a textarea with no id; FilterRulesField and ColorPropField in the same file already mint with React.useId(). No aria-describedby or error wiring existed on the field.",
"summary": "InspectorJsonField mints an id with React.useId() and pairs Label htmlFor with the textarea id. All three doors that draw the editor (curated kind json props, the other-arm JSON fallback in renderField, the Advanced GenericPropField) go through the one component, so all are named. New pin PageBlockInspector.jsonFieldLabel-27178.test.tsx finds the add and Sort editors with getByLabelText.",
"tests": "New file 2/2 passed. Every PageBlockInspector test file at bc69c4ad: 13 files, 86 tests passed (verify lock VERDICT command-exit 0). pnpm --filter @object-ui/app-shell run type-check (src and tsconfig.test.json): exit 0. eslint on both changed files: exit 0, zero warnings in the new test. Ablation via scripts/ablation-replace.mjs on committed 616a9071 (drop the htmlFor and id pair): anchor 1 to 0, blob e552a33e to 1da05b77, both tests red with 'Found a label with the text of: Add existing records (picker), however no form control was found associated to that label' and the same for Sort; restored, blob equals HEAD blob e552a33e, git diff HEAD empty. App-shell closure built under the lock first (9m43s).",
"gates": "dispatch-gates --commands at e781cf8f: 24 families, all run, all exit 0 (check-sdui-manifest and its --self-test included; ui vitest scripts/ under the lock). dispatch-gates --ran: 24 derived, 24 run, 0 NOT-MEASURED, exit 0. dispatch-gates --tier: no path-derived mandate. CI families: in_progress at report time, read from PR 27254 check-runs.",
"files_changed": [
"ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx (+6 / -1)",
"ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.jsonFieldLabel-27178.test.tsx (+102 new)"
],
"line_budget": "109 changed lines (+108 / -1) at e781cf8f vs the 3000 human-merge threshold: under; no governed path; no ratchet file touched",
"changeset": "none; @object-ui/app-shell is private; skip-changeset declared at pr_create and read back on the PR",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 GitHub MCP calls (subscribe_pr_activity on PR 27254 is the claude-code-remote tool, not GitHub MCP)",
"api_writes": "2: pr_create through the fleet-write relay (run 40693260434, 5502 bytes sent and stored, identical; assignee os-warren); this report through post-stamped. git push is not a REST write.",
"deviations": [
"PR body folds .github/PULL_REQUEST_TEMPLATE.md headings (What changes, Verification, Human Minimum) into the os-dev section order: the os-dev exception",
"First pnpm install failed on a network reset fetching node headers for better-sqlite3; the retry passed (frozen lockfile both times)",
"origin/main was merged twice (docs-only landings) to keep the gate derivation off a stale tree; final head e781cf8f is a merge commit",
"Full app-shell pnpm test not run locally: scoped to every PageBlockInspector test file in the directory (13 files); the package suite is CI's Test UI"
],
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: Studio, page designer, block inspector: the Columns (FieldListField), string-list and array prop labels in PageBlockInspector.tsx name no control or group; getByLabelText('Columns') finds the label and no control · evidence: three Label elements with no htmlFor or id remain in PageBlockInspector.tsx at e781cf8f, each heading a list of rows, so the fix is a group name (role group plus aria-labelledby), not a for pairing · dedupe words: PageBlockInspector FieldListField string-list array Label group accessible name",
"carrier: none · noted, not filed: the jsonEditorUnder helper comment in PageBlockInspector.relatedListProps-27054.test.tsx still says the label is a sibling, not a for; out of date after this PR, helper still works"
],
"body_amendments": [
"Verification section: '108 changed lines' should read '109 changed lines' (the derivation that printed 108 ran before the test-typing commit bc69c4ad added one line)"
]
}
Activity
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: p3 ·
domain:ui·area:studio· 2026-10-28T09:56Z⛔ Not a claim.
InspectorJsonField'sLabelhas nohtmlFor/idpairing, so curated JSON props have no accessible name. It is a11y with no wrong data, so p3.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#1·session_01JQwxbZeBv2y91towxasSUT· 2026-10-28T10:00ZYou are the os-dev developer agent for exactly ONE card: #27178, 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/NNNNN" -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 "Dispatch the ui lane" one message with your branch name and wait. Worktreeobjectstack-issue-27178on that branch from a freshorigin/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=27178 --file=F --jsonrun ONCE with output captured to a file (timestamps only as{{NOW}}/{{WAS:…}}). ⛔ 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, 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. Changeset in the root.changeset/when a published package changes. ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.Ruling (cannot be re-ruled)
Triage first grade (2026-10-28 09:56Z, triage#1): p3,
domain:ui,area:studio. Verbatim: "InspectorJsonField'sLabelhas nohtmlFor/idpairing, so curated JSON props have no accessible name. It is a11y with no wrong data, so p3." No maintainer ruling.PM mechanism assumptions (measure on
origin/main2f96d874; disproof welcome)ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx≈L137InspectorJsonFieldrendersLabelbeside its textarea with no pairing. Pair them the way the inspector's other rows do (find the shared row helper, e.g.InspectorTextField, and its id scheme —React.useIdor the existing idPath helper); prefer reusing that helper's pairing over a new scheme.Label+ control pairs with no association; fix only those inPageBlockInspector.tsx, and list the rest in the report as findings.Scope
File surface:
PageBlockInspector.tsx(InspectorJsonFieldonly), + tests. Tests:getByLabelText('Sort')(or another curated JSON prop) finds the textarea; one other inspector row unchanged (control). Ablation: drophtmlFor⇒ the label test goes red. Stay under 300 changed lines.Serial: seat 4's PR #27163 (#27054, draft) edits
PageBlockInspector.tsxat the import block and ≈L541–700 (it adds newInspectorJsonFieldcall sites), not theInspectorJsonFielddefinition. Mergeorigin/mainbefore your gates; if #27163 lands first, merge again and confirm its new call sites get the pairing too.Tier
Quote
dispatch-gates --tieron your surface. Expected none.TIER_DEFAULT, effortlow.Clause-②: no,Metadata-protocol: none.@object-ui/app-shellis private:skip-changeset.objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#5·session_01JQwxbXGpVJHZTXXerMoY4C· 2026-10-28T11:46ZYou are the os-dev developer agent for exactly ONE card: #27178, 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/27178" -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 another ui lane seat" one message with your branch name and wait. Worktreeobjectstack-issue-27178on that branch from a freshorigin/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=27178 --file=F --jsonrun ONCE with output captured to a file (timestamps only as{{WAS:…}}/ the now-token). ⛔ 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 #27178, 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;node scripts/check-sdui-manifest.mjsis a gate on every ui card. Changeset in the root.changeset/when a published package changes (@object-ui/i18npatch for namespace rows; 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 "Take another ui lane seat" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.Shared-file rules for every Studio Interfaces card this round (from ui seat 1, which holds the neighbouring work):
engine.studio.*rows, the Interfaces familyengine.studio.if.*included, out ofui/packages/app-shell/src/views/metadata-admin/i18n.tsintoui/packages/i18n/src/namespaces/studio/{en,zh}.ts. ⛔ Add or edit no row inmetadata-admin/i18n.ts; write new rows only in the studio namespace files, and a zh test preloads withawait loadStudioCatalogue('zh').ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsxis shared with ui seat 1's Studio publish refusals: shown only as a ~4 s toast with engine-internal text; the Pending changes panel keeps no refusal; "Show me" does not focus the offending input and the dashboard/report refusal strip has no "Show me" #26730 (the Interfaces refusal strip ≈L4683 gets anonShow), Studio rails: a search with no matches shows a blank list with no "no matches" message (object rail, permission-set rail) #26733 (props on the fourStudioRailcall sites, Interfaces ≈L5104) and PR fix(metadata-protocol,runtime,app-shell)!: refuse a save or draft publish naming a package that is not installed #26920 (≈L1270–1330), plus the other seat-5 Interfaces cards named below. Keep your hunks inside your declared region; whoever lands second mergesorigin/mainand resolves.Supersedes ui seat 1's order 6466033575 on this card, by agreement with seat 1: seat 5 takes #27178, and seat 1 takes #27177 under its own order 6466026033. Its serial note about #27163 is moot.
Shared files:
ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsxwas last edited by seat 4's PR #27163 (#27054, merged 2026-10-28T11:02Z). Seat 1's #27177 (the page designer canvas) runs beside this card on other files (previews/). Measure open PRs on the file at your first edit.PM mechanism assumptions (measure; disproof welcome)
On
origin/main2d05cd47:InspectorJsonField(PageBlockInspector.tsx≈L170) draws<Label>{label}</Label>with nohtmlFor, beside its textarea with noid. Its sibling rows in the same file (≈L244, ≈L262) already pair label and control withReact.useId(). The fix is that pairing:useId()→Label htmlFor↔ textareaid. Keep any existingaria-describedby/ error wiring.Scope
File surface:
PageBlockInspector.tsx(InspectorJsonFieldonly), a test beside it. Test (acceptance):getByLabelTextfinds the JSON textarea of a curated JSON prop (e.g.record:related_list'sadd, or asort/columnsJSON fallback) by its label, and the "Found a label … however no form control was found" warning is gone. Ablation: drop thehtmlFor/idpair and the test turns red.Tier
No path-derived mandate expected.
TIER_DEFAULT, effortlow(XS).Clause-②: no,Metadata-protocol: none.@object-ui/app-shellisprivate: true:skip-changeset.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 10
Session:
session_01JQwxbXGpVJHZTXXerMoY4CAccount:
os-warrenBranch:
claude/project-thread-qresy5Worktree:
objectstack-issue-27178Domain:
domain:uiSeat:
domain:ui#5File surface:
ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx(InspectorJsonFieldonly), a test beside it (stop on breach; explain in the report)Container & model:
XS,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate expected),effort: lowClause-②: no
Metadata-protocol: none
Responsibility:
ui/packages/app-shellStudio page block inspector produces it (this lane) | a curated JSON prop's textarea has no accessible name | every page block prop drawn byInspectorJsonFieldThread-read: 6468168275
Premise: holds (
InspectorJsonField≈L170 draws<Label>with nohtmlForand a textarea with noid; siblings ≈L244/≈L262 pair withuseId(); onorigin/main2d05cd47; readings in dispatch order 6468168275)Serial constraints cleared: seat 4's PR #27163 on this file merged; ui seat 1 takes #27177 (other files) and agreed this card is seat 5's; seat 1's order 6466033575 is superseded by 6468168275.
Dispatch order: comment 6468168275. Card thread
claude/project-thread-qresy5may start editing.objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 27178, "status": "done", "branch": "claude/project-thread-qresy5", "pr": "https://github.com/objectstack-ai/objectstack/pull/27254", "head": "e781cf8f", "session": "session_01JQwxbW6EicqdfE9onssvrp", "premise_still_valid": true, "mechanism": "PM assumption confirmed on origin/main dfe85c9a: InspectorJsonField drew Label with no htmlFor beside a textarea with no id; FilterRulesField and ColorPropField in the same file already mint with React.useId(). No aria-describedby or error wiring existed on the field.", "summary": "InspectorJsonField mints an id with React.useId() and pairs Label htmlFor with the textarea id. All three doors that draw the editor (curated kind json props, the other-arm JSON fallback in renderField, the Advanced GenericPropField) go through the one component, so all are named. New pin PageBlockInspector.jsonFieldLabel-27178.test.tsx finds the add and Sort editors with getByLabelText.", "tests": "New file 2/2 passed. Every PageBlockInspector test file at bc69c4ad: 13 files, 86 tests passed (verify lock VERDICT command-exit 0). pnpm --filter @object-ui/app-shell run type-check (src and tsconfig.test.json): exit 0. eslint on both changed files: exit 0, zero warnings in the new test. Ablation via scripts/ablation-replace.mjs on committed 616a9071 (drop the htmlFor and id pair): anchor 1 to 0, blob e552a33e to 1da05b77, both tests red with 'Found a label with the text of: Add existing records (picker), however no form control was found associated to that label' and the same for Sort; restored, blob equals HEAD blob e552a33e, git diff HEAD empty. App-shell closure built under the lock first (9m43s).", "gates": "dispatch-gates --commands at e781cf8f: 24 families, all run, all exit 0 (check-sdui-manifest and its --self-test included; ui vitest scripts/ under the lock). dispatch-gates --ran: 24 derived, 24 run, 0 NOT-MEASURED, exit 0. dispatch-gates --tier: no path-derived mandate. CI families: in_progress at report time, read from PR 27254 check-runs.", "files_changed": [ "ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx (+6 / -1)", "ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.jsonFieldLabel-27178.test.tsx (+102 new)" ], "line_budget": "109 changed lines (+108 / -1) at e781cf8f vs the 3000 human-merge threshold: under; no governed path; no ratchet file touched", "changeset": "none; @object-ui/app-shell is private; skip-changeset declared at pr_create and read back on the PR", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 GitHub MCP calls (subscribe_pr_activity on PR 27254 is the claude-code-remote tool, not GitHub MCP)", "api_writes": "2: pr_create through the fleet-write relay (run 40693260434, 5502 bytes sent and stored, identical; assignee os-warren); this report through post-stamped. git push is not a REST write.", "deviations": [ "PR body folds .github/PULL_REQUEST_TEMPLATE.md headings (What changes, Verification, Human Minimum) into the os-dev section order: the os-dev exception", "First pnpm install failed on a network reset fetching node headers for better-sqlite3; the retry passed (frozen lockfile both times)", "origin/main was merged twice (docs-only landings) to keep the gate derivation off a stale tree; final head e781cf8f is a merge commit", "Full app-shell pnpm test not run locally: scoped to every PageBlockInspector test file in the directory (13 files); the package suite is CI's Test UI" ], "open_questions": [], "out_of_scope_findings": [ "class: a · reach: Studio, page designer, block inspector: the Columns (FieldListField), string-list and array prop labels in PageBlockInspector.tsx name no control or group; getByLabelText('Columns') finds the label and no control · evidence: three Label elements with no htmlFor or id remain in PageBlockInspector.tsx at e781cf8f, each heading a list of rows, so the fix is a group name (role group plus aria-labelledby), not a for pairing · dedupe words: PageBlockInspector FieldListField string-list array Label group accessible name", "carrier: none · noted, not filed: the jsonEditorUnder helper comment in PageBlockInspector.relatedListProps-27054.test.tsx still says the label is a sibling, not a for; out of date after this PR, helper still works" ], "body_amendments": [ "Verification section: '108 changed lines' should read '109 changed lines' (the derivation that printed 108 ran before the test-typing commit bc69c4ad added one line)" ] }