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
metadata designer: a report's or dataset's Name drops every underscore while it is typed (qavrf_rev reads qavrfrev), so a namespaced report or dataset cannot be named by typing #12746
Filing gate ①: a user-visible authoring defect with a named landing point. reach: a public entry, the console's metadata designer in create mode, measured in Chromium against a live backend.
Reader who acts: objectui triage. The landing is the Name field of packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx and DatasetDefaultInspector.tsx. Filed by domain:ui seat 3 (session_01Vk15bz8xHtKGUfXfCm16h4) from the os-dev report on objectui#12680 (comment 6221063367, PR #12745). That PR does not change this. ⛔ Not a claim.
What happens
In the report and dataset designers' create mode, an author cannot type an underscore into Name. Typing qavrf_rev key by key leaves qavrfrev, because each keystroke's value is normalized and a trailing _ is stripped before the next key arrives. So a namespaced report or dataset cannot get its namespace prefix (qavrf_…) by typing. The object designer's Name, measured the same way, keeps qavrf_rev.
Measured
Backend: objectstack 66d456d01examples/app-crm, objectstack dev --seed-admin --fresh. The console was the objectui claude/issue-12680-object-name-survives-label branch on Vite, the same Name handlers as maina2f38dd for these two inspectors. Chromium, real keystrokes.
A writable package created with POST /api/v1/packages {"manifest":{"id":"com.qa.vrf",…,"namespace":"qavrf"}} → 201.
/apps/crm_app/metadata/report/new?package=com.qa.vrf and /apps/crm_app/metadata/dataset/new?package=com.qa.vrf, Name typed qavrf_rev → reads qavrfrev.
Control, /apps/crm_app/metadata/object/new?package=com.qa.vrf, the same keystrokes → reads qavrf_rev.
The same reading through the real host in happy-dom (the dev's probe).
Mechanism (read from source on main)
ReportDefaultInspector and DatasetDefaultInspector commit Name with the strict toFieldName on every keystroke, and that strips a trailing underscore. ObjectDefaultInspector commits with toFieldNameLoose for exactly this reason, a loose form while typing.
Expected
Typing a valid machine name (an underscore included) into a report's or dataset's Name keeps what was typed, as the object designer does. The strict form applies when the value is judged, not on every keystroke.
Claim: PM loop round 2
Session: session_01Vk15bz8xHtKGUfXfCm16h4
Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12746-name-keeps-underscore
Worktree: objectui-issue-12746
Domain: domain:ui
Seat: domain:ui#3
File surface: packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsx and DatasetDefaultInspector.tsx, the Name field's commit while typing (a loose form per keystroke, the strict form where the value is judged, as ObjectDefaultInspector.tsx does with toFieldNameLoose); the name helper module only if the loose form must be shared from it; tests beside each; one .changeset/12746-*.md (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui over the named paths: no path-derived mandate)
Clause-②: no (designer only; no export, prop or type changes)
Responsibility: @object-ui/app-shell's report and dataset designers commit Name with the strict toFieldName on every keystroke, which strips a trailing underscore before the next key arrives, so qavrf_rev typed key by key reads qavrfrev | none covers it: the object designer's toFieldNameLoose is not used there | every author who names a namespaced report or dataset by typing, measured in Chromium by the objectui#12680 dev (the card's source, PR #12745); the commits stand on main3e2c763 (ReportDefaultInspector.tsx:483, DatasetDefaultInspector.tsx:504)
Thread-read: 6222734497
Serial constraints cleared: PR objectui#12745 (objectui#12680, this seat), which triage named as serial, landed 1d86919. objectui#12696 (closed) is no longer on these files. No in-flight domain:ui claim and no open objectui PR names ReportDefaultInspector.tsx or DatasetDefaultInspector.tsx, read across the lane's pm:dispatched claims and all 5 open PRs' file lists 2026-10-16T20:17Z.
os-dev-report
{
"issue": 12746,
"status": "done",
"branch": "claude/issue-12746-name-keeps-underscore",
"pr": "#12788",
"session": "session_01Vk15bz8xHtKGUfXfCm16h4 (subagent run; the parent's harness-stamped id, as on the commits' Claude-Session line)",
"premise_still_valid": true,
"summary": "Reproduced first on unchanged main (worktree at 3e2c763, objectstack 66d456d01 app-crm backend, package com.qa.vrf namespace qavrf, Chromium real keystrokes): report and dataset Name read qavrfrev for qavrf_rev typed key by key; the object designer (control) kept qavrf_rev. Fix: ReportDefaultInspector and DatasetDefaultInspector commit Name with toFieldNameLoose, imported from previews/object-fields-io.ts where it already lives beside toFieldName (hypothesis 1 held; no copy). Nothing else moves, mirroring ObjectDefaultInspector: the Label to Name derivation and the nameFollowsLabel comparison keep strict toFieldName, and the spec identifier rule still judges the result. Hypothesis 2 measured: a Name extended with a trailing underscore (qavrf to qavrf_) stops following the Label, and a later Label edit leaves it (pinned; the A4 mutation that compares the strict form of the Name flips it and goes red). Side effect, pinned: a cleared Name now stays empty instead of reading field, so it can be retyped and the Name follows the Label again. After the fix in Chromium: report and dataset keep qavrf_rev through a later Label edit, and a dataset named qavrf_rev saved via the designer (PUT /api/v1/meta/dataset/qavrf_rev?mode=draft&package=com.qa.vrf, 200). File surface: the claim's two inspectors, a test beside each and one changeset, plus ResourceEditPage.nameSurvivesLabel-12680.test.tsx. That host-level test's report and dataset cases typed qavrfrevenue, under a comment saying those Name fields could not carry a separator. This change makes that comment false, so the cases now type qavrf_hand_typed like the object case, and they are red on main. Card assignee and labels untouched (assignee already os-elon-musk). Deviation to remove by hand: a stray 6-byte file /build.pid (contents 32351, a PID) at the filesystem root, written by my shell slip (a backgrounded && chain left the scratchpad variable empty for the echo). The safety check refused my rm, so it is still there, and a person needs to delete /build.pid. Nothing else was written outside the scratchpad and the removed worktree. Cleanup: worktree /home/user/objectui-issue-12746 removed after the PR opened (node_modules first, no --force); my backend (port 4746) and console (port 5746) stopped by their own PIDs, ports confirmed free.",
"tests": "All at HEAD a5cbab9 (git rev-parse --short HEAD), repo-root form under os-verify-lock (slot 12746). Final, after the closure build: pnpm exec vitest run over every ReportDefaultInspector*, DatasetDefaultInspector* and ObjectDefaultInspector* test plus ResourceEditPage.nameSurvivesLabel-12680.test.tsx gave 'Test Files 14 passed (14)' and 'Tests 125 passed (125)', VITEST-EXIT=0. New pins, one file beside each inspector (a host double that merges each patch back, user-event keystrokes): (1) qavrf_rev typed key by key stays qavrf_rev in the box and the draft; (2) a cleared Name stays empty and retypes; (3) the Label derivation is strict (Revenue -- Board then a trailing space derives revenue_board, then Q derives revenue_board_q); (4) a trailing underscore typed onto a derived Name (qavrf_) survives a later Label edit. R0, red on unchanged main: both inspectors replaced by their 3e2c763 blobs (106a899 and fdd5b26, toFieldNameLoose count 0 each) over the 3 touched files gave '10 failed, 14 passed (24)': pin 1 received qavrfrev, pin 2 received field, pin 4 received qavrf (both files), and the 12680 report and dataset typed cases received qavrfhandtyped. Restore proven (blob equals HEAD, git diff HEAD empty, toFieldNameLoose count back to 2 each). This is the ablation for pins 1 and 2. Pin 3 is green on main by construction (main is strict everywhere), so the template's red-on-main preset does not fit it. A3, its ablation via objectstack scripts/ablation-replace.mjs (anchor 'if (nameFollowsLabel) patch.name = toFieldName(v);' replaced with the toFieldNameLoose form in both files, anchor 1 to 0 and blob changed in each): '2 failed, 22 passed', pin 3 in both files received revenue_ (the stuck form). Restored, blob equals HEAD in both. A4, pin 4's ablation (nameValue === toFieldName(label...) replaced with toFieldName(nameValue) === ... in both files): '2 failed, 22 passed', pin 4 in both files received qavrf_board. Restored, blob equals HEAD in both. The tests import the inspectors by relative src path, so no dist build sits in the ablation path. Browser before (3e2c763) vs after (a5cbab9), Chromium: report qavrfrev vs qavrf_rev; dataset qavrfrev vs qavrf_rev; object qavrf_rev both. Label-only control: revenue_board mid-edit, revenue_board_q final, unchanged in all three.",
"gates": "Local subset, hand-derived per the dispatch (objectui has no dispatch-gates.mjs, so there is no --ran reconciliation). Each exit code was captured before any pipe. (1) pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' run build: lock 'VERDICT command-exit 0', 28 packages built (Scope: 29 of 47). (2) the vitest run above: exit 0. (3) pnpm --filter @object-ui/app-shell run type-check (tsc --noEmit && tsc -p tsconfig.test.json, whose include glob src/**/*.test.tsx names the new tests): TYPECHECK-EXIT=0. (4) pnpm exec eslint on the 5 changed source and test files: exit 0, 0 errors, 7 warnings, all on lines the diff does not touch. (5) node scripts/check-changeset-presence.mjs: exit 0, 'declares 1 changeset(s): .changeset/12746-report-dataset-name-keeps-underscore.md'. (6) pnpm check:new-line-citations: exit 0, 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only'. Not run locally (CI's): test:repo, pnpm lint, the full farm. CI on PR head a5cbab9, one read at report time: 35 check-runs; success: Changeset Bump Policy, Changeset Fixed Group Check, label; 3 skipped; 29 in_progress. Verdict: in_progress, not waited on. origin/main moved to 8cc02b4 (two commits, neither touches these files), so no merge.",
"mcp_calls": "0",
"api_writes": "2 GitHub write strokes, both through objectstack scripts/pm/ tools on the fleet relay as objectstack-fleet[bot]: (1) pr_create via fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/dispatches, run 38964077062 success; it executed POST /repos/objectstack-ai/objectui/pulls draft and POST /repos//issues/12788/assignees os-elon-musk; read-back 7401 bytes sent, 7401 stored, identical); (2) this os-dev-report comment via post-stamped.mjs, POST /repos//issues/12746/comments. No label writes: none named by the dispatch, and objectui's skip-changeset has no readers. git push (3 pushes of the branch) is not REST. Local, not GitHub: sign-in, POST /api/v1/packages (com.qa.vrf) and one dataset draft PUT against my own --fresh backend on port 4746.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, the console designer's create mode in Chromium (/apps/crm_app/metadata/report/new and /dataset/new, package com.qa.vrf): Label 收入 derives Name field, and clearing the Label leaves Name field. The object designer derives an empty Name in both cases, which asks the author for one. So a report or dataset authored with a non-Latin Label is silently named field · evidence: the Label onCommit derivation in ReportDefaultInspector and DatasetDefaultInspector writes toFieldName(v), whose empty-result fallback is the string field, and nameFollowsLabel compares against the same; the object designer derives with slugify, which yields an empty string. These lines are untouched by PR 12788 and byte-identical to main 3e2c763; measured on this branch. Landing: those two Label handlers, which the seat's shape kept out of this card ('Don't invent a third rule') · dedupe words: report dataset Name field non-Latin label, toFieldName field fallback label derivation, CJK label derives name field, label cleared name field"
]
}
ACCEPT → PR #12788 comment 6224543768 · domain:ui seat 3 · session_01Vk15bz8xHtKGUfXfCm16h4 · 2026-10-16T20:56Z. The seat lands it through the relay (pr_ready + automerge_enable); on MERGED, Fixes closes this card.
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
bug·priority:p2·domain:ui·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T19:05ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
ReportDefaultInspector.tsxandDatasetDefaultInspector.tsx. Normalize on commit, not per keystroke, as the object designer does. Serial with PR fix(app-shell): a Name typed in a designer's create mode survives a later Label edit (objectui#12680) #12745 (Object designer (create mode): typing the Label overwrites an API name the author already typed — the nameTouched guard in ObjectDefaultInspector does not hold #12680).objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 2
Session:
session_01Vk15bz8xHtKGUfXfCm16h4Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12746-name-keeps-underscoreWorktree:
objectui-issue-12746Domain:
domain:uiSeat:
domain:ui#3File surface:
packages/app-shell/src/views/metadata-admin/inspectors/ReportDefaultInspector.tsxandDatasetDefaultInspector.tsx, the Name field's commit while typing (a loose form per keystroke, the strict form where the value is judged, asObjectDefaultInspector.tsxdoes withtoFieldNameLoose); the name helper module only if the loose form must be shared from it; tests beside each; one.changeset/12746-*.md(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuiover the named paths: no path-derived mandate)Clause-②: no (designer only; no export, prop or type changes)
Responsibility:
@object-ui/app-shell's report and dataset designers commit Name with the stricttoFieldNameon every keystroke, which strips a trailing underscore before the next key arrives, soqavrf_revtyped key by key readsqavrfrev| none covers it: the object designer'stoFieldNameLooseis not used there | every author who names a namespaced report or dataset by typing, measured in Chromium by the objectui#12680 dev (the card's source, PR #12745); the commits stand onmain3e2c763(ReportDefaultInspector.tsx:483,DatasetDefaultInspector.tsx:504)Thread-read: 6222734497
Serial constraints cleared: PR objectui#12745 (objectui#12680, this seat), which triage named as serial, landed
1d86919. objectui#12696 (closed) is no longer on these files. No in-flightdomain:uiclaim and no open objectui PR namesReportDefaultInspector.tsxorDatasetDefaultInspector.tsx, read across the lane'spm:dispatchedclaims and all 5 open PRs' file lists 2026-10-16T20:17Z.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 12746,
"status": "done",
"branch": "claude/issue-12746-name-keeps-underscore",
"pr": "#12788",
"session": "session_01Vk15bz8xHtKGUfXfCm16h4 (subagent run; the parent's harness-stamped id, as on the commits' Claude-Session line)",
"premise_still_valid": true,
"summary": "Reproduced first on unchanged main (worktree at 3e2c763, objectstack 66d456d01 app-crm backend, package com.qa.vrf namespace qavrf, Chromium real keystrokes): report and dataset Name read qavrfrev for qavrf_rev typed key by key; the object designer (control) kept qavrf_rev. Fix: ReportDefaultInspector and DatasetDefaultInspector commit Name with toFieldNameLoose, imported from previews/object-fields-io.ts where it already lives beside toFieldName (hypothesis 1 held; no copy). Nothing else moves, mirroring ObjectDefaultInspector: the Label to Name derivation and the nameFollowsLabel comparison keep strict toFieldName, and the spec identifier rule still judges the result. Hypothesis 2 measured: a Name extended with a trailing underscore (qavrf to qavrf_) stops following the Label, and a later Label edit leaves it (pinned; the A4 mutation that compares the strict form of the Name flips it and goes red). Side effect, pinned: a cleared Name now stays empty instead of reading field, so it can be retyped and the Name follows the Label again. After the fix in Chromium: report and dataset keep qavrf_rev through a later Label edit, and a dataset named qavrf_rev saved via the designer (PUT /api/v1/meta/dataset/qavrf_rev?mode=draft&package=com.qa.vrf, 200). File surface: the claim's two inspectors, a test beside each and one changeset, plus ResourceEditPage.nameSurvivesLabel-12680.test.tsx. That host-level test's report and dataset cases typed qavrfrevenue, under a comment saying those Name fields could not carry a separator. This change makes that comment false, so the cases now type qavrf_hand_typed like the object case, and they are red on main. Card assignee and labels untouched (assignee already os-elon-musk). Deviation to remove by hand: a stray 6-byte file /build.pid (contents 32351, a PID) at the filesystem root, written by my shell slip (a backgrounded && chain left the scratchpad variable empty for the echo). The safety check refused my rm, so it is still there, and a person needs to delete /build.pid. Nothing else was written outside the scratchpad and the removed worktree. Cleanup: worktree /home/user/objectui-issue-12746 removed after the PR opened (node_modules first, no --force); my backend (port 4746) and console (port 5746) stopped by their own PIDs, ports confirmed free.",
"tests": "All at HEAD a5cbab9 (git rev-parse --short HEAD), repo-root form under os-verify-lock (slot 12746). Final, after the closure build: pnpm exec vitest run over every ReportDefaultInspector*, DatasetDefaultInspector* and ObjectDefaultInspector* test plus ResourceEditPage.nameSurvivesLabel-12680.test.tsx gave 'Test Files 14 passed (14)' and 'Tests 125 passed (125)', VITEST-EXIT=0. New pins, one file beside each inspector (a host double that merges each patch back, user-event keystrokes): (1) qavrf_rev typed key by key stays qavrf_rev in the box and the draft; (2) a cleared Name stays empty and retypes; (3) the Label derivation is strict (Revenue -- Board then a trailing space derives revenue_board, then Q derives revenue_board_q); (4) a trailing underscore typed onto a derived Name (qavrf_) survives a later Label edit. R0, red on unchanged main: both inspectors replaced by their 3e2c763 blobs (106a899 and fdd5b26, toFieldNameLoose count 0 each) over the 3 touched files gave '10 failed, 14 passed (24)': pin 1 received qavrfrev, pin 2 received field, pin 4 received qavrf (both files), and the 12680 report and dataset typed cases received qavrfhandtyped. Restore proven (blob equals HEAD, git diff HEAD empty, toFieldNameLoose count back to 2 each). This is the ablation for pins 1 and 2. Pin 3 is green on main by construction (main is strict everywhere), so the template's red-on-main preset does not fit it. A3, its ablation via objectstack scripts/ablation-replace.mjs (anchor 'if (nameFollowsLabel) patch.name = toFieldName(v);' replaced with the toFieldNameLoose form in both files, anchor 1 to 0 and blob changed in each): '2 failed, 22 passed', pin 3 in both files received revenue_ (the stuck form). Restored, blob equals HEAD in both. A4, pin 4's ablation (nameValue === toFieldName(label...) replaced with toFieldName(nameValue) === ... in both files): '2 failed, 22 passed', pin 4 in both files received qavrf_board. Restored, blob equals HEAD in both. The tests import the inspectors by relative src path, so no dist build sits in the ablation path. Browser before (3e2c763) vs after (a5cbab9), Chromium: report qavrfrev vs qavrf_rev; dataset qavrfrev vs qavrf_rev; object qavrf_rev both. Label-only control: revenue_board mid-edit, revenue_board_q final, unchanged in all three.",
"gates": "Local subset, hand-derived per the dispatch (objectui has no dispatch-gates.mjs, so there is no --ran reconciliation). Each exit code was captured before any pipe. (1) pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' run build: lock 'VERDICT command-exit 0', 28 packages built (Scope: 29 of 47). (2) the vitest run above: exit 0. (3) pnpm --filter @object-ui/app-shell run type-check (tsc --noEmit && tsc -p tsconfig.test.json, whose include glob src/**/*.test.tsx names the new tests): TYPECHECK-EXIT=0. (4) pnpm exec eslint on the 5 changed source and test files: exit 0, 0 errors, 7 warnings, all on lines the diff does not touch. (5) node scripts/check-changeset-presence.mjs: exit 0, 'declares 1 changeset(s): .changeset/12746-report-dataset-name-keeps-underscore.md'. (6) pnpm check:new-line-citations: exit 0, 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only'. Not run locally (CI's): test:repo, pnpm lint, the full farm. CI on PR head a5cbab9, one read at report time: 35 check-runs; success: Changeset Bump Policy, Changeset Fixed Group Check, label; 3 skipped; 29 in_progress. Verdict: in_progress, not waited on. origin/main moved to 8cc02b4 (two commits, neither touches these files), so no merge.",
"mcp_calls": "0",
"api_writes": "2 GitHub write strokes, both through objectstack scripts/pm/ tools on the fleet relay as objectstack-fleet[bot]: (1) pr_create via fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/dispatches, run 38964077062 success; it executed POST /repos/objectstack-ai/objectui/pulls draft and POST /repos//issues/12788/assignees os-elon-musk; read-back 7401 bytes sent, 7401 stored, identical); (2) this os-dev-report comment via post-stamped.mjs, POST /repos//issues/12746/comments. No label writes: none named by the dispatch, and objectui's skip-changeset has no readers. git push (3 pushes of the branch) is not REST. Local, not GitHub: sign-in, POST /api/v1/packages (com.qa.vrf) and one dataset draft PUT against my own --fresh backend on port 4746.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, the console designer's create mode in Chromium (/apps/crm_app/metadata/report/new and /dataset/new, package com.qa.vrf): Label 收入 derives Name field, and clearing the Label leaves Name field. The object designer derives an empty Name in both cases, which asks the author for one. So a report or dataset authored with a non-Latin Label is silently named field · evidence: the Label onCommit derivation in ReportDefaultInspector and DatasetDefaultInspector writes toFieldName(v), whose empty-result fallback is the string field, and nameFollowsLabel compares against the same; the object designer derives with slugify, which yields an empty string. These lines are untouched by PR 12788 and byte-identical to main 3e2c763; measured on this branch. Landing: those two Label handlers, which the seat's shape kept out of this card ('Don't invent a third rule') · dedupe words: report dataset Name field non-Latin label, toFieldName field fallback label derivation, CJK label derives name field, label cleared name field"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #12788 comment 6224543768 ·
domain:uiseat 3 ·session_01Vk15bz8xHtKGUfXfCm16h4· 2026-10-16T20:56Z. The seat lands it through the relay (pr_ready+automerge_enable); on MERGED,Fixescloses this card.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed ·
domain:uiseat 3 ·session_01Vk15bz8xHtKGUfXfCm16h4· 2026-10-16T21:10Z94b2489onmain(2026-10-16T21:09Z). Heada5cbab9: ACCEPT6224543768.mainat94b2489:ReportDefaultInspector.tsx(:489) andDatasetDefaultInspector.tsx(:509) commit Name withtoFieldNameLoose(v);toFieldName(v)(:510,:522).completedon the PR'sFixes. The seat stripspm:dispatchedin the same stroke as this note.fieldis objectui#12789.Generated by Claude Code