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.
ObjectMapSchema(types)声明的键几乎与 ObjectMap 实际读的键无交集:map 块整体未建模,组件 props 类型写的是 ObjectGridSchema,每处 map 读取都走 as any #5018
3. 组件 props 类型 —— ObjectMapProps.schema 声明成 ObjectGridSchema(ObjectMap.tsx:61),不是 ObjectMapSchema。于是全部 map 专有读取都得绕过类型:(schema as any).map、(schema as any).enableClustering、(schema as any).navigation、(schema as any).style、(schema as any).mapStyle。
Triage: needs-user-decision, type Bug. This is the map component's contract-shape fork: three authorities (declared ObjectMapSchema = 4 keys; runtime reads ~15 keys via as any; props typed as ObjectGridSchema), plus the card's item 3 — top-level spellings vs map block, with top-level silently winning. Choosing the canonical shape expands/reshapes the declared public face ⇒ human floor, not auto-adjudicable.
Four-facet block:
Platform long-term coherence: today's shape is three truths for one component; any ruling (model the map block in types, or canonicalize top-level) shrinks special cases. Doing nothing grows them — every new map key will be born untyped.
AI-agent error-resistance: strongest axis. Declared-loose + consumed-via-as any + silent-wrong is exactly the shape where AI-authored metadata hides errors. A closed MapConfigSchema lifted to types with the props type corrected makes the wrong spelling unrepresentable at authoring time.
Startup scope discipline: option 2 (lift MapConfigSchema to types, wire ObjectMapSchema.map to it, fix the props type) is alignment of declaration with existing behavior, not new capability. The item-3 precedence question should be ruled narrow: pick one canonical authoring shape, keep the other as internal/deprecated — do not declare both.
Recommendation: option 2 as the core (declare what is consumed; smallest true fix), with item 3 ruled "map block is canonical; top-level spellings become internal (ObjectView flattening) and undocumented, refused or warned on direct authoring". Option 1's full key-by-key modeling can follow as mechanical work once the shape is ruled. Related cards: #5017 (the style collision leg — dispatchable now, its pin survives either ruling), #5003/#5020 (findings, same component).
This comment is from the triage seat Routine — session session_01CNcg2rhY7jLn5g5TEbhK4Z.
Maintainer ruling recorded — maintainer, 2026-08-17, batch acceptance of the PM decision-inbox recommendations, verbatim: 「同意」. Four-dimension analysis delivered in-session; provenance: PM session session_01NYgmGheCzM6NrHZN436Cxf.
Ruled: full alignment (direction 1), with point 3 decided as follows:
ObjectMapSchema gains the keys the renderer actually reads: staticData / data / filter / sort / map: MapConfigSchema / enableClustering / navigation; MapConfigSchema is lifted from plugin-map into types so the declared face and the runtime validation share one schema; ObjectMapProps.schema becomes ObjectMapSchema and every as any map read goes.
The map block is the author face. The top-level flat spelling is an internal form (the ObjectView flatten product) — it stays out of ObjectMapSchema and out of the docs. When both are present, the map block wins, with a dev-mode warning naming the shadowed top-level keys — what the author wrote outranks what internal flattening produced. The existing pin at ObjectMap.filterConfig.test.tsx:164 records the OLD precedence; it flips with this ruling (the implementer cites this comment in the pin's history note). If a constraint surfaces that makes the flip unsafe for ObjectView's producer path, stop and report — do not silently keep the old precedence.
Label needs-user-decision → pm:queue in the same stroke.
Container & model: claude-opus-5, under the measured fable-quota exemption.
Tier, judged from content rather than paths
No path-derived mandate applies (objectui carries no .claude/skills/pm-dispatch/** surface). But clause ② fires and I am recording it explicitly rather than letting the absence of a path mandate read as a clearance: this card adds seven keys to ObjectMapSchema — a published authoring type — and flips a precedence rule that decides which of two spellings a renderer honours. That is both a public-surface widening and a change to accept/reject behaviour. Clause ② is judged from card content, and this is what it is for.
Fable is exhausted for this session (measured twice tonight: #9415, #9420 — provider refusal, quoted on those cards). Under the sole recorded exit, the card moves to opus, never lower.
Compensating measure
Clause ② exists because a widened public surface is expensive to walk back — every key declared becomes a key authors write and a key the platform owes. With the tier safeguard reduced, the PM seat substitutes a direct one:
I will check the resulting declared surface key-by-key against what ObjectMap.tsx actually reads — that the widening declares exactly the consumed set and nothing more. A key declared but unread re-creates this card's defect pointing the other way, and it is the specific failure a lower tier is most likely to introduce here, because "add the keys" invites adding the plausible ones.
That review happens before landing.
The ruling being dispatched
Recorded above (maintainer, 2026-08-17, 「同意」): direction 1, full alignment, with the precedence point decided — the map block is the author face, the flat top-level spelling is an internal form (ObjectView's flatten product), and when both are present the map block wins, with a dev-mode warning naming the shadowed keys.
Two things the implementing seat must not soften:
The pin at ObjectMap.filterConfig.test.tsx:164 records the OLD precedence and flips with this ruling. It is updated with a history note citing the ruling comment — not deleted, and not left asserting the old order.
The stop-and-report condition is live: if a constraint surfaces making the flip unsafe for ObjectView's producer path (ObjectView.tsx:770-776 is the flattener), the seat stops and reports. It does not silently keep the old precedence — a ruling quietly not applied is worse than one reported as blocked.
Related and deliberately not pulled in: #5017 (the style / mapStyle collision leg) is dispatchable independently and its pin survives either ruling; #5003 / #5020 are findings on the same component. Keeping them separate so this card's surface stays reviewable.
{
"issue": 5018,
"status": "done",
"branch": "claude/issue-5018-objectmap-schema-alignment",
"pr": "https://github.com/objectstack-ai/objectui/pull/5156",
"premise_still_valid": true,
"summary": "Implemented the ruling in full: ObjectMapSchema gained the seven keys the renderer reads (data, staticData, filter, sort, map, enableClustering, navigation), the package-private zod was lifted into @object-ui/types so the declared face and the runtime validation are one schema, ObjectMapProps.schema is ObjectMapSchema, and every (schema as any) map read is gone; getMapConfig now checks the map block FIRST and warns (dev-mode, warn-once) naming the ignored top-level keys. The lifted symbol had to be renamed ObjectMapConfig / ObjectMapConfigSchema rather than the MapConfigSchema the ruling names, because @objectstack/spec/automation already exports that name and check:spec-symbols refused it verbatim on the first run; the ruling's intent (ONE schema shared by both layers) is unchanged. Two sub-premises of the card did not survive verification and are reported rather than papered over: the :164 pin does NOT record the old precedence (it authors no map block, so it is unchanged by the flip and the old order was in fact unpinned), and a misspelled TOP-LEVEL key is still not a compile error because BaseSchema carries [key: string]: any - only the map BLOCK is closed, which is what makes the card's headline latitudeFieId typo a compile error. Contract-vs-prompt conflict noted per the standing clause: the dispatch prompt specified the bare Claude Code footer, the standing contract requires the session-URL form because the bare one is stripped on every later edit; the contract won.",
"tests": "All at final head aad4609, tree clean (git status --porcelain empty; ablations restored byte-identically before the union). BUILD FIRST: pnpm --filter '@object-ui/plugin-map^...' build exit 0. pnpm --filter @object-ui/plugin-map test exit 0 (8 files / 52 tests). pnpm --filter @object-ui/plugin-view test exit 0 (12 files / 114 tests). pnpm exec turbo run type-check exit 0, 81/81 - this is the DOWNSTREAM consumer sweep (every package depending on @object-ui/types). pnpm exec turbo run build --filter='!@object-ui/site' exit 0, 43/43. lint on plugin-map + types + plugin-view exit 0, 0 errors. NOTE: @object-ui/types has NO test script - 'pnpm --filter @object-ui/types test' matched zero scripts and exited 0 silently (the documented zero-match trap); its suites run from the root vitest project, so the touched file was run directly: npx vitest run packages/types/src/__tests__/view-navigation-config-spec-parity.test.ts exit 0, 5 passed. GATE UNION at aad4609, every check:* in package.json, all exit 0: control-bytes 0, spec-symbols 0, phantom-deps 0, self-import 0, published-dist 0, doc-types 0, i18n-keys 0, i18n-drift 0, i18n-dead-keys 0, action-forward-parity 0, skills-paths 0. Two gates were RED on the first pass and are recorded because they changed the implementation: check:spec-symbols exit 1 ('@object-ui/types declares 2 spec-named symbols the spec already owns: interface MapConfig ... const MapConfigSchema ... exported by @objectstack/spec/automation') forced the rename; check:published-dist exit 1 ('the workspace build failed ... nothing trustworthy to inspect') on UNLOADABLE_DEPENDENCY - a vite string alias matches by PREFIX, so @object-ui/types/zod resolved to the src/zod DIRECTORY ('Is a directory (os error 21)') in the four configs that alias types to source; fixed by adding the /zod subpath alias before the bare package in all four, mirroring the form already pinned at vitest.config.mts:250-251. REVERSE VERIFICATION, direction predicted before each run, all four matched: (1) restore the old branch order in getMapConfig -> predicted 2 red + controls green -> observed exactly 2 failed / 50 passed (expected '1' to be '40'; expected '' to contain '[ObjectMap]'); (2) delete the warnOnShadowedFlatMapKeys call -> predicted 1 red -> observed 1 failed / 51 passed; (3) make the flattener emit map: viewOptions.map -> predicted red in ObjectView.mapFlatten -> observed 2 failed / 112 passed (expected true to be false on hasOwnProperty('map')); (4) correct latitudeFieId to latitudeField in the type pin -> predicted TS2578 -> observed 'src/ObjectMap.schemaAlignment.test.tsx(121,7): error TS2578: Unused @ts-expect-error directive.' No dogfood ablation was involved, so no dist rebuild applies. STOP-AND-REPORT CONDITION NOT REACHED: both flatteners (plugin-view ObjectView case 'map', plugin-list ListView case 'map') spread options.map's CONTENTS flat and emit NO map key, so the reordered branch is unreachable for their output and the flip cannot change what any ObjectView/ListView map renders; that property is now pinned in plugin-view/src/__tests__/ObjectView.mapFlatten.test.tsx rather than left to be re-derived.",
"declared_vs_consumed": [
"type -> ObjectMap.tsx:170,:173 (warn memo) | was declared | still declared",
"objectName -> :115 getDataConfig, :497, :555 | was declared | still declared",
"data -> :104 getDataConfig, :491 | was UNDECLARED | now ViewData",
"staticData -> :108 getDataConfig | was UNDECLARED | now any[]",
"filter -> :161 legacy probe, :467 $filter | was UNDECLARED | now any[] (mirrors ObjectGridSchema)",
"sort -> :468 $orderby | was UNDECLARED | now string | SortConfig[]",
"map -> :199 (.style), :217 | was UNDECLARED | now ObjectMapConfig",
"enableClustering -> :570 | was UNDECLARED | now boolean",
"navigation -> :554 | was UNDECLARED | now ViewNavigationConfig (4th site; the parity test gained a matching identity pin)",
"locationField -> :202,:204 internal flat form | was declared | kept, documented as internal",
"titleField -> :207 internal flat form | was declared | kept, documented as internal",
"mapStyle -> :199 | was declared | still declared",
"style -> :199 | inherited from BaseSchema | UNCHANGED - the collision is #5017's, read keeps its exact prior runtime behaviour with one narrow cast pointing at that card",
"NOT declared, per ruling item 4: flat latitudeField / longitudeField / descriptionField / zoom / center (:202-213) - declared in the consumer as FlatMapConfigKeys instead"
],
"card_key_list_discrepancies": [
"Card named, nothing to do: `style` / `mapStyle` at :199 - mapStyle was already declared, and `style` is BaseSchema's inline-CSS key whose collision is #5017. Not touched.",
"Code reads, card missed: `type`, read by the legacy-warning memo at :170/:173. Already declared, so no action.",
"No key the card named turned out to have no read site; the card's list was accurate apart from the two notes above.",
"Card's claim about the :164 pin is the one that failed verification - see below."
],
"pin_164_outcome": "NOT what the card said. ObjectMap.filterConfig.test.tsx:164 is it('keeps the top-level latitudeField branch intact') and its schema carries no `map` block, so it never recorded a precedence at all and is unchanged by the flip - the old top-level-wins order was UNPINNED. Kept (not deleted, not left asserting a stale order) and annotated with a history note citing the ruling comment, stating what it does and does not pin and where the precedence is now pinned in both directions. Its assertion still guards the thing that matters about that branch: the internal flat form has to keep rendering or every ObjectView/ListView map goes blank.",
"open_questions": [
{
"question": "ObjectMapSchema still declares the flat `locationField` and `titleField`. The ruling says the flat spelling 'stays out of ObjectMapSchema', but it also says the schema 'gains' keys - it orders no removal, and these two were published before the ruling. Keep them declared-but-documented-as-internal, or remove them?",
"options": [
"A - keep both, marked as the internal flatten form (what this PR does): non-breaking, and both have real read sites at :202-207 so they pass the declared-equals-consumed check",
"B - remove both for item-4 purity: a breaking change to a published type that the ruling did not order, and one that buys nothing at authoring time because BaseSchema's index signature accepts them anyway"
],
"recommendation": "A. B's only benefit would be authoring-time rejection, and BaseSchema's `[key: string]: any` means removal does NOT produce a type error - so B pays a breaking change for a guarantee it cannot deliver. If the maintainer wants the surface literally cleared, it should be a follow-up card ruled together with #5155."
},
{
"question": "Should ObjectMapConfigSchema be `.strict()`, so a misspelled key inside the `map` block is also diagnosed at RUNTIME (console.warn via the existing safeParse), not only at compile time?",
"options": [
"A - leave it permissive (what this PR does): the lift is behaviour-identical, and a strictness change is a separate accept/reject decision the ruling did not make",
"B - make it strict now: closes the card's headline symptom for JS authors and AI-generated metadata too, at the cost of newly warning on any extra key inside an authored `map` block"
],
"recommendation": "A for this PR, B as a follow-up worth ruling. B is the stronger position on the AI-error-resistance axis - a typed-only guarantee protects TypeScript authors and nobody else, and AI-authored metadata is exactly the traffic that arrives untyped - but it changes accept/reject behaviour on a published surface, which is the clause-2 shape that should be ruled rather than slipped in under an alignment card."
},
{
"question": "PM mechanism: the dispatch prompt specified the bare `_Generated by [Claude Code](https://claude.ai/code)_` footer, while the standing contract requires the session-URL form because the bare one is stripped from PR bodies on every later edit. The contract was followed. Should dispatch prompts stop restating the footer?",
"options": [
"A - drop the footer block from dispatch prompts and let the contract own it",
"B - restate it in the session-URL form in the prompt template"
],
"recommendation": "A. A restated constant is a second place to drift, and this one had already drifted; the contract is where the reason for the session-URL form is written down."
}
],
"out_of_scope_findings": [
"filed as #5155: BaseSchema's `[key: string]: any` leaves every component schema open, so a 'declare the surface' fix can never reject a misspelled TOP-LEVEL key - measured here (TS2578 on two pins that assumed otherwise), labelled `finding`, unassigned, no pm:queue. Searched the 99 open `finding`-labelled cards first; #5068 and #4605 are the nearest and are different layers."
]
}
Activity
os-zhuang commentedon Aug 17, 2026
Triage:
needs-user-decision, type Bug. This is the map component's contract-shape fork: three authorities (declaredObjectMapSchema= 4 keys; runtime reads ~15 keys viaas any; props typed asObjectGridSchema), plus the card's item 3 — top-level spellings vsmapblock, with top-level silently winning. Choosing the canonical shape expands/reshapes the declared public face ⇒ human floor, not auto-adjudicable.Four-facet block:
mapblock in types, or canonicalize top-level) shrinks special cases. Doing nothing grows them — every new map key will be born untyped.mapblock, showcase uses maps today, and a typo'dlatitudeFieIdcurrently renders a silent empty map. This is not speculative surface; it is the typed face for behavior that already ships.as any+ silent-wrong is exactly the shape where AI-authored metadata hides errors. A closedMapConfigSchemalifted to types with the props type corrected makes the wrong spelling unrepresentable at authoring time.MapConfigSchemato types, wireObjectMapSchema.mapto it, fix the props type) is alignment of declaration with existing behavior, not new capability. The item-3 precedence question should be ruled narrow: pick one canonical authoring shape, keep the other as internal/deprecated — do not declare both.Recommendation: option 2 as the core (declare what is consumed; smallest true fix), with item 3 ruled "map block is canonical; top-level spellings become internal (ObjectView flattening) and undocumented, refused or warned on direct authoring". Option 1's full key-by-key modeling can follow as mechanical work once the shape is ruled. Related cards: #5017 (the
stylecollision leg — dispatchable now, its pin survives either ruling), #5003/#5020 (findings, same component).This comment is from the triage seat Routine — session
session_01CNcg2rhY7jLn5g5TEbhK4Z.Generated by Claude Code
yinlianghui commentedon Aug 17, 2026
标签半状态修复(objectui 分片 PM,session
session_01GTRjn8xBqp75dk7kFupVRt):分诊席 2026-08-17 15:47Z 的评论已裁「三份形状真相 + 顶层/map块静默优先级 = 公开契约收束,human floor」并宣布needs-user-decision,但标签半边未落、卡仍挂pm:queue—— 批次 22 选席时差点按可派态误读。已按分诊裁定补齐:pm:queue→needs-user-decision。裁决点与推荐方案见上一条分诊评论(推荐:修法 2 为核,item 3 收窄为「map块为规范拼法,顶层拼法降为内部形态」)。待维护者裁定后再入队。Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Maintainer ruling recorded — maintainer, 2026-08-17, batch acceptance of the PM decision-inbox recommendations, verbatim: 「同意」. Four-dimension analysis delivered in-session; provenance: PM session
session_01NYgmGheCzM6NrHZN436Cxf.Ruled: full alignment (direction 1), with point 3 decided as follows:
ObjectMapSchemagains the keys the renderer actually reads:staticData/data/filter/sort/map: MapConfigSchema/enableClustering/navigation;MapConfigSchemais lifted fromplugin-mapintotypesso the declared face and the runtime validation share one schema;ObjectMapProps.schemabecomesObjectMapSchemaand everyas anymap read goes.mapblock is the author face. The top-level flat spelling is an internal form (the ObjectView flatten product) — it stays out ofObjectMapSchemaand out of the docs. When both are present, themapblock wins, with a dev-mode warning naming the shadowed top-level keys — what the author wrote outranks what internal flattening produced. The existing pin atObjectMap.filterConfig.test.tsx:164records the OLD precedence; it flips with this ruling (the implementer cites this comment in the pin's history note). If a constraint surfaces that makes the flip unsafe for ObjectView's producer path, stop and report — do not silently keep the old precedence.Label
needs-user-decision→pm:queuein the same stroke.Generated by Claude Code
os-zhuang commentedon Aug 18, 2026
Claimed — PM dispatch.
session_01NYgmGheCzM6NrHZN436Cxfclaude/issue-5018-objectmap-schema-alignmentclaude-opus-5, under the measured fable-quota exemption.Tier, judged from content rather than paths
No path-derived mandate applies (objectui carries no
.claude/skills/pm-dispatch/**surface). But clause ② fires and I am recording it explicitly rather than letting the absence of a path mandate read as a clearance: this card adds seven keys toObjectMapSchema— a published authoring type — and flips a precedence rule that decides which of two spellings a renderer honours. That is both a public-surface widening and a change to accept/reject behaviour. Clause ② is judged from card content, and this is what it is for.Fable is exhausted for this session (measured twice tonight: #9415, #9420 — provider refusal, quoted on those cards). Under the sole recorded exit, the card moves to opus, never lower.
Compensating measure
Clause ② exists because a widened public surface is expensive to walk back — every key declared becomes a key authors write and a key the platform owes. With the tier safeguard reduced, the PM seat substitutes a direct one:
That review happens before landing.
The ruling being dispatched
Recorded above (maintainer, 2026-08-17, 「同意」): direction 1, full alignment, with the precedence point decided — the
mapblock is the author face, the flat top-level spelling is an internal form (ObjectView's flatten product), and when both are present themapblock wins, with a dev-mode warning naming the shadowed keys.Two things the implementing seat must not soften:
ObjectMap.filterConfig.test.tsx:164records the OLD precedence and flips with this ruling. It is updated with a history note citing the ruling comment — not deleted, and not left asserting the old order.ObjectView's producer path (ObjectView.tsx:770-776is the flattener), the seat stops and reports. It does not silently keep the old precedence — a ruling quietly not applied is worse than one reported as blocked.Related and deliberately not pulled in: #5017 (the
style/mapStylecollision leg) is dispatchable independently and its pin survives either ruling; #5003 / #5020 are findings on the same component. Keeping them separate so this card's surface stays reviewable.Generated by Claude Code
os-zhuang commentedon Aug 18, 2026
{ "issue": 5018, "status": "done", "branch": "claude/issue-5018-objectmap-schema-alignment", "pr": "https://github.com/objectstack-ai/objectui/pull/5156", "premise_still_valid": true, "summary": "Implemented the ruling in full: ObjectMapSchema gained the seven keys the renderer reads (data, staticData, filter, sort, map, enableClustering, navigation), the package-private zod was lifted into @object-ui/types so the declared face and the runtime validation are one schema, ObjectMapProps.schema is ObjectMapSchema, and every (schema as any) map read is gone; getMapConfig now checks the map block FIRST and warns (dev-mode, warn-once) naming the ignored top-level keys. The lifted symbol had to be renamed ObjectMapConfig / ObjectMapConfigSchema rather than the MapConfigSchema the ruling names, because @objectstack/spec/automation already exports that name and check:spec-symbols refused it verbatim on the first run; the ruling's intent (ONE schema shared by both layers) is unchanged. Two sub-premises of the card did not survive verification and are reported rather than papered over: the :164 pin does NOT record the old precedence (it authors no map block, so it is unchanged by the flip and the old order was in fact unpinned), and a misspelled TOP-LEVEL key is still not a compile error because BaseSchema carries [key: string]: any - only the map BLOCK is closed, which is what makes the card's headline latitudeFieId typo a compile error. Contract-vs-prompt conflict noted per the standing clause: the dispatch prompt specified the bare Claude Code footer, the standing contract requires the session-URL form because the bare one is stripped on every later edit; the contract won.", "tests": "All at final head aad4609, tree clean (git status --porcelain empty; ablations restored byte-identically before the union). BUILD FIRST: pnpm --filter '@object-ui/plugin-map^...' build exit 0. pnpm --filter @object-ui/plugin-map test exit 0 (8 files / 52 tests). pnpm --filter @object-ui/plugin-view test exit 0 (12 files / 114 tests). pnpm exec turbo run type-check exit 0, 81/81 - this is the DOWNSTREAM consumer sweep (every package depending on @object-ui/types). pnpm exec turbo run build --filter='!@object-ui/site' exit 0, 43/43. lint on plugin-map + types + plugin-view exit 0, 0 errors. NOTE: @object-ui/types has NO test script - 'pnpm --filter @object-ui/types test' matched zero scripts and exited 0 silently (the documented zero-match trap); its suites run from the root vitest project, so the touched file was run directly: npx vitest run packages/types/src/__tests__/view-navigation-config-spec-parity.test.ts exit 0, 5 passed. GATE UNION at aad4609, every check:* in package.json, all exit 0: control-bytes 0, spec-symbols 0, phantom-deps 0, self-import 0, published-dist 0, doc-types 0, i18n-keys 0, i18n-drift 0, i18n-dead-keys 0, action-forward-parity 0, skills-paths 0. Two gates were RED on the first pass and are recorded because they changed the implementation: check:spec-symbols exit 1 ('@object-ui/types declares 2 spec-named symbols the spec already owns: interface MapConfig ... const MapConfigSchema ... exported by @objectstack/spec/automation') forced the rename; check:published-dist exit 1 ('the workspace build failed ... nothing trustworthy to inspect') on UNLOADABLE_DEPENDENCY - a vite string alias matches by PREFIX, so @object-ui/types/zod resolved to the src/zod DIRECTORY ('Is a directory (os error 21)') in the four configs that alias types to source; fixed by adding the /zod subpath alias before the bare package in all four, mirroring the form already pinned at vitest.config.mts:250-251. REVERSE VERIFICATION, direction predicted before each run, all four matched: (1) restore the old branch order in getMapConfig -> predicted 2 red + controls green -> observed exactly 2 failed / 50 passed (expected '1' to be '40'; expected '' to contain '[ObjectMap]'); (2) delete the warnOnShadowedFlatMapKeys call -> predicted 1 red -> observed 1 failed / 51 passed; (3) make the flattener emit map: viewOptions.map -> predicted red in ObjectView.mapFlatten -> observed 2 failed / 112 passed (expected true to be false on hasOwnProperty('map')); (4) correct latitudeFieId to latitudeField in the type pin -> predicted TS2578 -> observed 'src/ObjectMap.schemaAlignment.test.tsx(121,7): error TS2578: Unused @ts-expect-error directive.' No dogfood ablation was involved, so no dist rebuild applies. STOP-AND-REPORT CONDITION NOT REACHED: both flatteners (plugin-view ObjectView case 'map', plugin-list ListView case 'map') spread options.map's CONTENTS flat and emit NO map key, so the reordered branch is unreachable for their output and the flip cannot change what any ObjectView/ListView map renders; that property is now pinned in plugin-view/src/__tests__/ObjectView.mapFlatten.test.tsx rather than left to be re-derived.", "declared_vs_consumed": [ "type -> ObjectMap.tsx:170,:173 (warn memo) | was declared | still declared", "objectName -> :115 getDataConfig, :497, :555 | was declared | still declared", "data -> :104 getDataConfig, :491 | was UNDECLARED | now ViewData", "staticData -> :108 getDataConfig | was UNDECLARED | now any[]", "filter -> :161 legacy probe, :467 $filter | was UNDECLARED | now any[] (mirrors ObjectGridSchema)", "sort -> :468 $orderby | was UNDECLARED | now string | SortConfig[]", "map -> :199 (.style), :217 | was UNDECLARED | now ObjectMapConfig", "enableClustering -> :570 | was UNDECLARED | now boolean", "navigation -> :554 | was UNDECLARED | now ViewNavigationConfig (4th site; the parity test gained a matching identity pin)", "locationField -> :202,:204 internal flat form | was declared | kept, documented as internal", "titleField -> :207 internal flat form | was declared | kept, documented as internal", "mapStyle -> :199 | was declared | still declared", "style -> :199 | inherited from BaseSchema | UNCHANGED - the collision is #5017's, read keeps its exact prior runtime behaviour with one narrow cast pointing at that card", "NOT declared, per ruling item 4: flat latitudeField / longitudeField / descriptionField / zoom / center (:202-213) - declared in the consumer as FlatMapConfigKeys instead" ], "card_key_list_discrepancies": [ "Card named, nothing to do: `style` / `mapStyle` at :199 - mapStyle was already declared, and `style` is BaseSchema's inline-CSS key whose collision is #5017. Not touched.", "Code reads, card missed: `type`, read by the legacy-warning memo at :170/:173. Already declared, so no action.", "No key the card named turned out to have no read site; the card's list was accurate apart from the two notes above.", "Card's claim about the :164 pin is the one that failed verification - see below." ], "pin_164_outcome": "NOT what the card said. ObjectMap.filterConfig.test.tsx:164 is it('keeps the top-level latitudeField branch intact') and its schema carries no `map` block, so it never recorded a precedence at all and is unchanged by the flip - the old top-level-wins order was UNPINNED. Kept (not deleted, not left asserting a stale order) and annotated with a history note citing the ruling comment, stating what it does and does not pin and where the precedence is now pinned in both directions. Its assertion still guards the thing that matters about that branch: the internal flat form has to keep rendering or every ObjectView/ListView map goes blank.", "open_questions": [ { "question": "ObjectMapSchema still declares the flat `locationField` and `titleField`. The ruling says the flat spelling 'stays out of ObjectMapSchema', but it also says the schema 'gains' keys - it orders no removal, and these two were published before the ruling. Keep them declared-but-documented-as-internal, or remove them?", "options": [ "A - keep both, marked as the internal flatten form (what this PR does): non-breaking, and both have real read sites at :202-207 so they pass the declared-equals-consumed check", "B - remove both for item-4 purity: a breaking change to a published type that the ruling did not order, and one that buys nothing at authoring time because BaseSchema's index signature accepts them anyway" ], "recommendation": "A. B's only benefit would be authoring-time rejection, and BaseSchema's `[key: string]: any` means removal does NOT produce a type error - so B pays a breaking change for a guarantee it cannot deliver. If the maintainer wants the surface literally cleared, it should be a follow-up card ruled together with #5155." }, { "question": "Should ObjectMapConfigSchema be `.strict()`, so a misspelled key inside the `map` block is also diagnosed at RUNTIME (console.warn via the existing safeParse), not only at compile time?", "options": [ "A - leave it permissive (what this PR does): the lift is behaviour-identical, and a strictness change is a separate accept/reject decision the ruling did not make", "B - make it strict now: closes the card's headline symptom for JS authors and AI-generated metadata too, at the cost of newly warning on any extra key inside an authored `map` block" ], "recommendation": "A for this PR, B as a follow-up worth ruling. B is the stronger position on the AI-error-resistance axis - a typed-only guarantee protects TypeScript authors and nobody else, and AI-authored metadata is exactly the traffic that arrives untyped - but it changes accept/reject behaviour on a published surface, which is the clause-2 shape that should be ruled rather than slipped in under an alignment card." }, { "question": "PM mechanism: the dispatch prompt specified the bare `_Generated by [Claude Code](https://claude.ai/code)_` footer, while the standing contract requires the session-URL form because the bare one is stripped from PR bodies on every later edit. The contract was followed. Should dispatch prompts stop restating the footer?", "options": [ "A - drop the footer block from dispatch prompts and let the contract own it", "B - restate it in the session-URL form in the prompt template" ], "recommendation": "A. A restated constant is a second place to drift, and this one had already drifted; the contract is where the reason for the session-URL form is written down." } ], "out_of_scope_findings": [ "filed as #5155: BaseSchema's `[key: string]: any` leaves every component schema open, so a 'declare the surface' fix can never reject a misspelled TOP-LEVEL key - measured here (TS2578 on two pins that assumed otherwise), labelled `finding`, unassigned, no pm:queue. Searched the 99 open `finding`-labelled cards first; #5068 and #4605 are the nearest and are different layers." ] }Generated by Claude Code
docs(plugin-map): README 按现码重写,不再教包里不存在的那套 API (objectstack-ai#5002) (o…
docs(plugin-map): API provider 节如实化为未实现 (objectstack-ai#5019) (object…
fix(plugin-map): 顶层 `style` 是内联 CSS,不再被当作 MapLibre style 读 (objectsta…