Skip to content

test(plugin-map): point the reach test narration at where the 'Marker' placeholder actually lives - #6341

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-5977-mapconfig-reach-stale-narration
Aug 25, 2026
Merged

test(plugin-map): point the reach test narration at where the 'Marker' placeholder actually lives#6341
os-support-ai merged 1 commit into
mainfrom
claude/issue-5977-mapconfig-reach-stale-narration

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#5977

Comment-only change to one test file. Head 4cb788f4e.

1. Dispatch premise table

Every coordinate re-derived against origin/main @ 79ebf30d1 (the dispatch order and the claim comment were written against c38162d7c; main moved between them).

#assumptionverdictmeasurement
M1narrating file is packages/plugin-map/src/ObjectMap.listViewMapConfigReach.test.tsx, directly under src/, not src/__tests__/heldls packages/plugin-map/src/ — the file is a direct child; plugin-map has no src/__tests__/
M2narrating comment sits at roughly :136-141, not exactly :138heldthe comment block is :136-140; :141 is the fireEvent.click it precedes
M3getMapConfig is defined at ObjectMap.tsx:345heldfunction getMapConfig(schema: MapConfigSource): ObjectMapConfig { at :345
M4fallback: 'Marker' is at ObjectMap.tsx:771, outside getMapConfigheld:771, inside the marker-building useMemo that begins at :735getMapConfig ends at :415

The claim comment's corrected framing — misattributed, not deleted — holds. The 'Marker' literal is alive at :771; what the narration got wrong is whose literal it is.

2. What I established the two functions actually do

getMapConfig (ObjectMap.tsx:345-415) has three return paths, and none of them now forges a title binding:

  1. a declared map block wins outright — return { ...config, style: config.style || style }, so titleField is exactly what the author wrote and nothing more;
  2. the internal flat form carries titleField: schema.titleField with an in-code note reading No || 'name' (objectui#5953);
  3. the default branch sets coordinate keys and descriptionField only, with Deliberately NO titleField (objectui#5953).

The marker path (ObjectMap.tsx:735-800) no longer does a bare property read. At :769:

consttitle=getRecordDisplayName(objectSchema,record,{titleField: mapConfig.titleField,fallback: 'Marker',});

'Marker' is therefore an option handed to the resolver, not a getMapConfig literal. In packages/core/src/utils/record-title.ts the resolver checks options.titleField at step 0 (valueAt(record, options?.titleField) ?? valueAt(record, objectDef?.titleField)), so a declared binding still wins outright; options.fallback is only reached at the very floor — Record #<id> handles any record that has an id, so 'Marker' now surfaces only for a truly id-less record.

The historical half — measured, and it needed correcting too

The dispatch asked me to determine whether the history was still accurate. It was not, and this is exactly the merge the card body called out.

The forward is e2e8e68f1 (feat(plugin-list): read the spec's view-level map block on list views). At its parent:

  • read site (ObjectMap.tsx:668 at e2e8e68f1^): const title = mapConfig.titleField ? record[mapConfig.titleField] : 'Marker'; — so 'Marker' was the else-arm of the read site's ternary, reachable only when no binding existed;
  • getMapConfig's flat branch (:375) carried titleField: schema.titleField || 'name', and its default branch (:395) carried titleField: 'name'. 'name', never 'Marker', is what getMapConfig fell back to.

For this arm specifically: pre-forward ListView's case 'map' read schema.options?.map only and always emitted locationField: schema.options?.map?.locationField || 'location'. This test declares map at the schema root, so the block was dropped, locationField: 'location' still arrived, getMapConfig took the flat branch, and titleField came out as the forged 'name'. The fixture records are { id, title, blurb, location } — no name key. So the pre-forward title was undefined, not 'Marker'.

The old comment asserted the 'Marker' symptom and the undefined symptom as one thing. They are two branches with two visibly different outcomes.

3. The narration, before and after

Before (:136-140):

// The title is a real read, not a forwarded prop: `getMapConfig` resolves
// `titleField`, the marker transform reads `record[titleField]`, and the
// popup renders it. Before the forward landed this read `'Marker'` — the
// literal `getMapConfig` falls back to — which is the `undefined`/placeholder
// marker-title symptom the card was filed for.

After (:136-146):

// The title is a real read, not a forwarded prop: `getMapConfig` resolves
// the declared `titleField`, the marker transform hands it to
// `getRecordDisplayName` as `options.titleField` — step 0 of that resolver,
// so a declared binding still wins outright — and the popup renders it.
// Before the forward landed the block never reached `getMapConfig` at all:
// the flat branch forged `titleField: 'name'` (dropped by objectui#5953)
// and these records carry no `name`, so this read `undefined` — the
// marker-title symptom the card was filed for. `'Marker'` is not a
// `getMapConfig` literal and never was: it is `getRecordDisplayName`'s
// `fallback` option in the marker transform, reached now only by an
// id-less record.

The reason the discriminating arm exists is kept — the arm still pins that the title is a real read of the declared binding rather than a forwarded prop, and the pre-forward symptom is still narrated. Only the pointer and the symptom's identity changed.

4. Verification — and what is structurally blind here

The test suite cannot verify this change, and that is expected. Comments do not execute: the 92 assertions in packages/plugin-map produce byte-identical results before and after, by construction. A green run is evidence the change is inert, not evidence the new prose is true. Type-check is blind for the same reason one level down — TypeScript strips comments, so the file parses identically either way; it only proves I did not break the comment syntax. Lint, control-byte and vi.mock scans read the changed bytes but judge form, never claims. Changeset presence judges declaration, not content.

The actual verification is the code reading in §2getMapConfig's three branches at ObjectMap.tsx:345-415, the resolver call at :769-772, getRecordDisplayName's step ordering in packages/core/src/utils/record-title.ts, and git show e2e8e68f1^ for the pre-forward read site, flat branch and ListView map case. Every sentence in the new comment traces to one of those.

Gate table

Gate set derived from the job step lists under .github/workflows/. Exit codes captured before any pipe; verdict lines are each gate's own output.

CI job (workflow)command run locallyexitthe gate's own verdict line
Changeset Declaration (changeset-presence.yml)node scripts/check-changeset-presence.mjs0✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) … Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.
Changeset Bump Policy (changeset-guard.yml)node scripts/check-changeset-no-major.mjs0✅ No changeset declares a `major` bump.
Control Byte Scan (control-bytes.yml)node scripts/check-control-bytes.mjs0✅ check-control-bytes: OK (scanned 5225 tracked text file(s); skipped 85 binary).
Inert vi.mock Specifier Check (vi-mock-specifiers.yml)node scripts/check-vi-mock-specifiers.mjs0✅ check-vi-mock-specifiers: OK (3748 tracked source file(s), 2060 test-named; 439 carry a mock; 684 relative specifier(s) resolved …)
Shell Escape Residue (shell-escape-residue.yml)node scripts/check-shell-escape-residue.mjs0✅ check-shell-escape-residue: OK (4/4 root(s) resolved … 0 occurrence(s) outside a fence)
Type Check (ci.yml)pnpm --filter @object-ui/plugin-map run type-check0script echo > @object-ui/plugin-map@17.6.0 type-check> tsc --noEmit && tsc -p tsconfig.test.json, no diagnostics
Test (ci.yml)pnpm exec vitest run --maxWorkers=2 packages/plugin-map/0Test Files 16 passed (16) · Tests 92 passed (92)
Lint (lint.yml)pnpm --filter @object-ui/plugin-map run lint0✖ 126 problems (0 errors, 126 warnings) — all pre-existing; the three in this file are no-explicit-any at :47/:50/:54, far above the edit

Supporting evidence, since "green" is cheap and the ways to fake it are not:

  • the type-check is not blind at the parse level: tsc -p tsconfig.test.json --listFiles lists ObjectMap.listViewMapConfigReach.test.tsx (1 hit), so the edited file really is in that program;
  • the test file really ran: packages/plugin-map/src contains exactly 16 test files and vitest reported 16 passed (16);
  • the gate union was re-run on the final commit4cb788f4e, and git status --porcelain plus git diff HEAD --stat are both empty, so the heavy runs above measured exactly this commit's tree;
  • one NOT-MEASURED run, disclosed: my first attempt was pnpm --filter @object-ui/plugin-map exec vitest run, which the repo's own guard refused (vitest 调用被拒绝:从包目录跑 vitest 会静默跑错测试集, objectui#3378). That exit 1 is a refused invocation, not a red gate; it is superseded by the repo-root form in the table, which is what CI runs.

Declared narrowing: I ran the package-scoped equivalents (--filter @object-ui/plugin-map) rather than the repo-wide pnpm test / pnpm type-check / pnpm lint that CI runs across every package. The diff is one comment block in one package plus one changeset; CI runs the full farm on this PR regardless.

5. The changeset, and a correction about skip-changeset

Triage suggested the skip-changeset label. That label is a phantom in this repository — it exists as a label object but nothing reads it. scripts/__tests__/ci-cd-pipeline-doc.test.ts pins this deliberately:

content/docs/guide/ci-cd-pipeline.md must keep stating that nothing reads the skip-changeset label. The label object exists in this repository (auto-minted by being applied, objectui#4912) and agents are being told to use it, so a page that stops denying it leaves the label as the most authoritative-looking answer in reach.

Confirmed independently: neither .github/workflows/changeset-presence.yml nor scripts/check-changeset-presence.mjs mentions it. So I followed the gate's own verdict line instead and declared the change with an empty-frontmatter changeset (.changeset/plugin-map-reach-test-narration.md) — the route the gate names as "a complete answer". No label was applied or minted. No major anywhere; content/docs/releases/ untouched.

6. Out-of-scope finding — reported, not fixed

Per the dispatch, the sweep was not widened. Of the other files narrating getMapConfig, ListView.tsx, ListView.mapViewLevelConfig.test.tsx, ObjectMap.markerTitle.test.tsx and InterfaceListPage.mapConfig.test.tsx are all clean — they either discuss precedence (objectui#5018) or already speak of 'name' in the past tense. CHANGELOG.md was not touched.

One file does carry the same defect class, and it is more than prose — packages/app-shell/src/views/InterfaceListPage.tsx:230-241:

its getMapConfig fills an absent titleField with the LITERAL 'name', and the marker title is then a plain record[titleField] read. So for any object whose display field is not literally name, every marker popup titles itself undefined.

… it survives ListView's whitelisted flatten and reaches getMapConfig ahead of that 'name' literal. It is NOT the general fix — an ObjectMap that resolved titles through getRecordDisplayName like its siblings would not need a derived binding at all … Filed separately.

All three present-tense claims are false on main today, and the "filed separately" general fix has landed (objectui#5953, PR #5975). The prose is stale, but the live question underneath it is whether the derived title binding at that seam is now redundant — or worse, actively harmful: it lands as options.titleField, which the resolver evaluates at step 0, ahead of the object's own declared nameField. That is a behaviour question for triage, not a comment fix. Left entirely untouched for the PM to file.


Generated by Claude Code

…' placeholder actually lives
The comment above the discriminating `titleField` arm in
`ObjectMap.listViewMapConfigReach.test.tsx` said `'Marker'` was "the literal
`getMapConfig` falls back to". It never lived there. `getMapConfig` starts at
`ObjectMap.tsx:345`; the placeholder is `getRecordDisplayName`'s `fallback`
option in the marker-building transform some 400 lines away, and since
objectui#5953 `getMapConfig` forges no title binding on any branch.
The historical half was also merged with a different branch's symptom. Measured
at the parent of the forward commit (e2e8e68^): the read site was
`mapConfig.titleField ? record[mapConfig.titleField] : 'Marker'`, and
`getMapConfig`'s flat branch carried `titleField: schema.titleField || 'name'`.
Pre-forward `ListView` always emitted `locationField: 'location'`, so this arm
took that flat branch, got a forged `'name'`, and — these fixture records carry
no `name` key — read `undefined`, not `'Marker'`.
Also drops the now-stale "the marker transform reads `record[titleField]`": the
transform resolves through `getRecordDisplayName`, which checks
`options.titleField` at step 0, so the declared binding still wins outright and
the arm still pins a real read rather than a forwarded prop.
Comment only. No assertion, fixture or expected value changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3223.5 KB3266.6 KB
Main entry chunk (gzip)154.2 KB350 KB
Entry fileindex-CVFVnCcX.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)10.96KB4.16KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.08KB114.67KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)171.74KB47.48KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.21KB44.67KB
plugin-dashboard (index.js)133.35KB34.45KB
plugin-designer (index.js)211.95KB42.75KB
plugin-detail (index.js)245.10KB62.31KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)126.92KB30.85KB
plugin-gantt (index.js)164.14KB39.87KB
plugin-grid (index.js)201.21KB54.43KB
plugin-kanban (index.js)52.83KB14.55KB
plugin-list (index.js)111.94KB27.24KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.49KB7.59KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)84.55KB20.74KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)54.84KB18.43KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.35KB0.70KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.49KB2.14KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-support-ai
os-support-ai marked this pull request as ready for review August 25, 2026 14:36
@os-support-ai
os-support-ai added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit 766cf70Aug 25, 2026
28 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-5977-mapconfig-reach-stale-narration branch August 25, 2026 14:47
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ObjectMap.listViewMapConfigReach.test.tsx:138 narrates a getMapConfig literal in the present tense that no longer exists

2 participants

@os-support-ai@claude