Repository navigation
skills(ui) + examples: retire the deprecated objectName / viewType ListView spelling in one stroke — the published skill teaches it and three showcase pages repeat it #14343
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 2, 2026 Claim: skills lane seat —
pm:queue→pm:dispatchedand the assignee set in the same write. Dispatching one os-dev flight; the serial condition (PR #14338 merged) is long met andskills/objectstack-ui/rules/pages.mdis held by no open PR (PR #14781 holdsrules/list-views.mdonly).
Session:session_01LraLgQVGq8egUwfYZpbYt1
Branch:claude/issue-14343-listview-canonical-spelling
Worktree:objectstack-issue-14343
Clause-②: yes — the rename asserts that the contract's canonical spelling binds a<ListView>;needs:contract-reviewon both carriers, reviewed in-seat at PASS. Governedskills/**⇒ draft PR; review requests are the seat's step.Re-measured at
origin/maindf657d9df, 2026-09-03T01:19Z — the card's premise moved:viewTypeoccurs 0 times in the published ui prose (skills/objectstack-ui/**/*.mdexcluding the generated contract); the flights since the card removed it.<ListView objectName=…>survives atrules/pages.md:180(prose) and:218(the react example) —SKILL.mditself carries none, the skills(ui): split the entry into SKILL.md + rules/ under #14296 item 1 (package net −58 tokens, −0.17%) #14658 split moved the section.- The three showcase pages hold:
examples/app-showcase/src/ui/pages/crm-workbench.page.ts:88,renewals-pipeline.page.ts:135-137(the onlyviewType="grid"left) and:169,task-desk.page.ts:44.<ObjectForm objectName>/<ObjectChart objectName>on the same pages are not deprecated (references/react-blocks.md:88carries no marker) and stay. - The generated contract's rows:
references/react-blocks.md:61objectName→[DEPRECATED → data={{ provider: 'object', object }}],:62viewType→[DEPRECATED → type]. ⚠️ Two source facts that decide the route, for the dev to settle FIRST: (1) the spec's ownREACT_RECORD_BLOCK_ALTERNATIVES['record:related_list'](packages/spec/src/ui/react-blocks.ts:167) prescribes<ListView objectName=…>— the deprecated spelling — in a string the contract publishes; (2) objectuipackages/core/src/utils/normalize-list-view.tsfoldsfields→columnsand canonicalisesviewType, but shows no fold fromdata.objectintoobjectName, whilepackages/plugin-list/src/ListView.tsx:791/:978still readschema.objectName. Whether<ListView data={{ provider: 'object', object: 'x' }} type="grid">with noobjectNameactually binds is the fact. Route 1 (it binds at source): rename inrules/pages.mdand the three pages, Clause-② as above. Route 2 (it does not): the DEPRECATED marker is the false claim, the flight stops withneeds_decisionand evidence, nothing is renamed, and the seat files the spec-side twin card — a false claim matching a generated contract string is the spec lane's, per the programme's rule.- Budget:
rules/pages.mdis 5,501 tokens against its row of 5,501 (headroom 0) and the canonical spelling is longer, so route 1 pays inside the file by deleting a strict restatement; the example pages are not ratcheted. ⛔ No ceiling raise, no re-wrap as payment, no other example edits.
First line
Fixes #14343;skip-changeset(skills/**+examples/**publish nothing from any package).
Generated by Claude Code
Claim: dev seat (os-dev subagent of the dispatching PM session — same session id, distinct seat)
Session:session_01LraLgQVGq8egUwfYZpbYt1
Branch: none created
Worktree: none createdThe seat's claim comment ordered STEP 1 — settle the route at source — before any edit, and it settled as route 2: the canonical spelling the generated contract prescribes does not bind a
ListViewat objectui42c129b6. Route 2 forbids the rename, so no branch, no worktree, no edit, no PR. Both read-only checkouts are byte-clean (git status --porcelainempty in each).Naming a branch and a worktree that were never created would be a false record, so this claim names none.
Evidence, the reconfirmed site inventory, and the four-axis escalation are in the
os-dev-reportcomment that follows.
Generated by Claude Code
os-dev-report
{ "issue": 14343, "status": "needs_decision", "route": 2, "premise_still_valid": false, "branch": null, "head_sha": null, "pr": null, "mcp_calls": "3 (claim comment + this report comment + the read-back). Card body and all comments were read through the zero-quota public-page payload channel; both repos read through the local read-only checkouts.", "read_at": { "objectstack_origin_main": "dee4dd4ba33f8b98ff90e240327b7567adecc017", "objectui_origin_main": "42c129b64491c5b53a6eb45c521c94c1c20d1f15" }, "summary": "STEP 1 settled the fork as route 2 and the flight stopped before any edit. The canonical spelling the generated contract prescribes does not bind at source: objectui's ListView reads neither half. The object half never reaches the query (ListView.tsx has zero reads of data.object; both fetch effects early-return on a falsy schema.objectName and the find call passes schema.objectName), and no normalizer folds it (normalize-list-view.ts folds ten documented pairs, none of them data.object to objectName). The visualization half cannot reach the block at all on the react tier: the react-page wrapper gives the type slot to the SDUI discriminator and parks the author value under specType, which only plugin-charts reads. Nothing was renamed, no branch was created, no PR was opened; both checkouts are byte-clean.", "evidence": [ "objectui packages/plugin-list/src/ListView.tsx:1684 - the data-fetch effect: a falsy schema.objectName returns before any fetch (same guard at :1405 for the object-definition effect).", "objectui packages/plugin-list/src/ListView.tsx:1978 - the only query call is dataSource.find(schema.objectName, findParams).", "objectui packages/plugin-list/src/ListView.tsx - grep count of dataConfig.object / data.object across the whole 4132-line file: 0. Grep count of specType: 0.", "objectui packages/plugin-list/src/ListView.tsx:1639, :1588 - schema.data is consumed for provider 'value' and (gantt-only) provider 'api'. provider 'object' appears nowhere in packages/plugin-list/src at all.", "objectui packages/core/src/utils/normalize-list-view.ts:232-360 - normalizeListViewSchema's fold list is exhaustive and documented; there is no data.object to objectName fold (grep count of .object in the file: 0).", "objectui packages/core/src/utils/normalize-list-view.ts:248 and :359 - defaultViewKind is (!viewType || viewType === 'list') and then next.viewType is FORCED to 'grid'. With viewType absent, any author type is overridden to grid.", "objectui packages/core/src/adapters/resolveDataSource.ts:55-57 - provider 'object' returns the context adapter and DROPS viewData.object; the object name is never carried.", "objectui packages/components/src/renderers/layout/react-page.tsx:91-93 - the react-tier wrapper: the SDUI discriminator wins the type slot (type: tag) and the author's type is parked as specType.", "objectui - repo-wide census of specType readers under packages/ (tests and CHANGELOGs excluded): only plugin-charts/src/normalizeChartSchema.ts:277 and ChartRenderer.tsx:83. The ObjectChart precedent the spec comment cites does NOT transfer to ListView.", "objectui packages/plugin-list/src/ListView.tsx:2331-2332 and :2363-2366 - the grid sub-view gets objectName: schema.objectName from baseProps and is handed no data, so plugin-grid/src/ObjectGrid.tsx:1145 (which does implement the fold) cannot rescue the grid path either.", "objectui - six sibling blocks implement the data.object fold locally: plugin-calendar/src/ObjectCalendar.tsx:275 and :801, plugin-gantt/src/ObjectGantt.tsx:640, plugin-grid/src/ObjectGrid.tsx:1145, plugin-map/src/ObjectMap.tsx:671, plugin-tree/src/ObjectTree.tsx:357 / :429 / :538. The fold is a per-block responsibility and ListView never took it.", "objectui packages/plugin-list - zero tests exercise provider 'object' on ListView (git grep of \"provider: 'object'\" in packages/plugin-list: 0 hits). The only object-to-objectName mapping pinned for this block is the DIFFERENT key schema.dataSource, in ListView.elementDataSource.test.tsx:85-293 via packages/react/src/element-data-source/ElementDataSourceGate.tsx:202 and :211.", "objectui - grep of 11284 across packages/: 0 hits. objectui never received the consumer half of the convergence.", "objectstack packages/spec/src/ui/react-blocks.ts:253-289 - the deprecation as published: objectName is required AND deprecated, replacedBy data; viewType deprecated, replacedBy type. The code comment at :260-262 justifies type by the ObjectChart specType precedent, which the census above shows does not extend to this block.", "objectstack packages/lint/src/validate-react-page-props.ts:885-894 (boundObjectName) and :1132-1135 - the validator ACCEPTS the canonical spelling as satisfying the required binding, so os validate is green on a page that renders an empty list.", "objectstack packages/lint/src/validate-react-page-props.ts:876-880 - the comment asserts the canonical-first precedence mirrors 'the one-directional fold objectui's normalizeListViewSchema applies at the component boundary'. That fold does not exist at objectui 42c129b6.", "objectstack packages/spec/src/ui/view.zod.ts:1630-1660 - ListViewShapeSchema declares type (the view kind, default grid) and data, and declares NO objectName. objectName is a react-overlay-only prop, and the metadata tier works because the host stamps it (objectui packages/app-shell/src/views/ObjectView.tsx:1193, :1816, :1999, :2504), not because ListView reads data.object." ], "premise_false": [ "The card's scope assumed the DEPRECATED marker names a spelling that works. It does not: neither half of the canonical spelling binds a ListView at objectui 42c129b6.", "Renaming the four sites as dispatched would ship a published skill and three showcase pages that pass os validate and render an empty list - strictly worse than the current deprecated-but-working spelling." ], "site_inventory_reconfirmed_at_dee4dd4b": { "positive_control": "definePage matched 2x in each of the 20 examples/app-showcase/src/ui/pages/*.page.ts files", "skills/objectstack-ui/rules/pages.md": ["180 (prose)", "218 (react example)"], "viewType_in_rules_pages_md": 0, "examples/app-showcase/src/ui/pages/crm-workbench.page.ts": ["88"], "examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts": ["135-137 (viewType=grid at 137)", "169"], "examples/app-showcase/src/ui/pages/task-desk.page.ts": ["44"] }, "gates": [], "gates_note": "No gate was run and none was owed: route 2 produced an empty diff in every repo. git status is empty in both read-only checkouts, no worktree was created, and nothing was built or mutated. Reporting a green gate against an unmodified tree would measure nothing.", "token_delta": { "file": "skills/objectstack-ui/rules/pages.md", "before_bytes": 22002, "before_tokens": 5501, "ratchet_row": 5501, "headroom": 0, "after": "unchanged - no edit was made", "note": "The budget was never reached: the route fork stopped the flight before the payment question could arise." }, "tests": "None run, and none owed - the diff is empty. The route verdict rests on static source reading plus grep censuses whose counts are stated above with their pinned sha; each count was taken with git show/grep against a single pinned objectui commit (42c129b6) after noticing that the shared refs/remotes/origin/main moved mid-run (75e7436 -> 42c129b6, the family rule), so every citation is from one tree.", "open_questions": [ { "question": "The generated react-tier contract publishes objectName and viewType as DEPRECATED in favour of data={{ provider: 'object', object }} and type, and objectstack's own lint accepts the canonical spelling as satisfying the required binding - but objectui's ListView reads neither. The producer half of the #11284 convergence landed and the consumer half never did. Which way does it close?", "options": [ "A - Retract the deprecation at its source (spec lane): drop the two deprecated blocks and the summary sentence in packages/spec/src/ui/react-blocks.ts:253-289, and revert the lint's canonical-first boundObjectName branch and required-binding satisfaction (validate-react-page-props.ts:885-894, :1132-1135). objectName / viewType become plain spellings again. This card then closes as premise-false with nothing renamed; skills and examples are already correct.", "B - Land the consumer half in objectui FIRST (a data.provider==='object' fold to objectName, plus a specType/type read for the view kind, in ListView or normalizeListViewSchema), then re-dispatch this card to rename the four sites afterwards.", "C - Rename the four sites now as the card asks and let objectui catch up later." ], "recommendation": "A, and C is not a live option. Long-term soundness (weighted first, and it decides this): what is published today is a declared-but-unenforced contract of the exact ADR-0049 / AGENTS.md #0.1 class - and worse than the usual case, because objectstack's lint comment (validate-react-page-props.ts:876-880) asserts as fact a fold that does not exist in the sibling repo, so the tree now documents a false claim about code it does not own. Between A and B the tie-breaker is that the type half is not merely unimplemented, it is structurally obstructed: the SDUI envelope owns the type key by construction (react-page.tsx:91-93), so the 'canonical' spelling can only ever reach a block through the specType rescue - a per-block bridge exactly one of eight object-bound blocks implements. A vocabulary whose canonical spelling needs a rescue the deprecated spelling does not need is not the simpler contract it was sold as, so A is the honest smaller surface and B should be taken only if someone actually wants it. Hard-to-get-wrong for AI-authored apps points the same way and is what rules C out outright: today an AI author reads the published contract, writes the canonical spelling, os validate says OK, and the list renders empty with no diagnostic - a validator actively blessing a dead spelling is worse than a missing feature, and A deletes that in one edit while B leaves it standing until objectui ships and republishes. Real business need is measured zero: no ListView anywhere in the objectstack examples corpus writes the canonical spelling, objectui has no test for it, and all four real sites write the deprecated one - the deprecation was authored, not pulled. Startup scope discipline agrees: a deprecation window for a spelling with zero users is precisely the gradual migration the 2026-08-27 ruling declines. The three secondary axes therefore concur with the first rather than outvoting it. Whichever way it goes, one guard is worth having so the class cannot recur: the react-blocks generator should refuse to publish a deprecated.replacedBy whose replacement no renderer reads." }, { "question": "Under either route, packages/spec/src/ui/react-blocks.ts:167 (REACT_RECORD_BLOCK_ALTERNATIVES for record:related_list) prescribes the DEPRECATED spelling inside a string the contract publishes - the same string the skill's rules/pages.md:180 prose mirrors. Which is it?", "options": [ "Under A it is already correct and needs no edit.", "Under B it becomes the last published site of the retired spelling and must be renamed in the same PR as the objectui fold." ], "recommendation": "Follows the first decision; noted so it is not lost. It is spec-lane either way and this seat did not touch it." } ], "out_of_scope_findings": [ "objectui: type is silently lossy on a react page, not merely ignored. With viewType absent, normalize-list-view.ts:248 and :359 force viewType to 'grid', so a react page writing type=kanban on a ListView renders a GRID rather than failing - a wrong render, not an empty one. Reported here only; no card filed, per dispatch.", "objectstack: the metadata tier has the same hole by a different door. ListViewShapeSchema (view.zod.ts:1630-1660) declares data and no objectName, so a metadata PAGE carrying a list-view component bound with data={{provider:'object',object}} reaches ListViewBlock with no objectName and renders empty too. Metadata list views survive only because the host stamps objectName (ObjectView.tsx:1193 et al). Reported here only.", "objectui: the fold ListView is missing is duplicated by hand in six sibling blocks (ObjectCalendar, ObjectGantt, ObjectGrid, ObjectMap, ObjectTree, plus plugin-dashboard/src/utils.ts:15). If route B is ever taken, the fold belongs in normalizeListViewSchema or a shared helper, not a seventh copy. Reported here only." ] }The route in one paragraph
ListViewbinds its object throughschema.objectNameand nothing else. Both of its effects early-return on a falsyobjectName(ListView.tsx:1405,:1684) and the single query call isdataSource.find(schema.objectName, …)(:1978); the file has zero reads ofdata.objectin 4,132 lines, andnormalizeListViewSchemafolds ten documented pairs, none of them this one. The visualization half never even arrives: on a react page the wrapper hands thetypeslot to the SDUI discriminator and parks the author's value underspecType(react-page.tsx:91-93), andspecTypeis read by exactly one block in the repo —plugin-charts— not this one. WithviewTypethen absent,normalize-list-view.ts:248/:359force it to'grid'.The sharpest part is not the missing fold, it is that
os validateis green on the canonical spelling (validate-react-page-props.ts:885-894,:1132-1135), and the comment above it (:876-880) states as fact a fold that does not exist in objectui. So the rename this card asks for would have shipped a published skill and three showcase pages that validate clean and render nothing.Six sibling blocks (
ObjectCalendar,ObjectGantt,ObjectGrid,ObjectMap,ObjectTree, and dashboardutils.ts) each carry thedata.objectfold by hand — so the fold is a per-block duty, andListViewsimply never took it.git grep 11284across objectuipackages/: 0 hits.Nothing was renamed. No branch, no worktree, no PR;
git status --porcelainis empty in both checkouts.
Generated by Claude Code
Route 2 —
pm:dispatched→pm:blocked, same write; assignee kept. Skills-lane seat (sessionsession_01LraLgQVGq8egUwfYZpbYt1), verified against both repos, not the report.The flight stopped at its route fork with nothing edited (report 5518979760): the contract's canonical spelling does not bind a ListView at objectui
42c129b. Seat spot-check at 2026-09-03T01:37Z, same tree:packages/plugin-list/src/ListView.tsx:1405and:1684early-return on a falsyschema.objectName,:1978isdataSource.find(schema.objectName, …), and the file has 0 reads ofdata.object,specTypeorprovider === 'object';normalize-list-view.tshas 0.objectand forces the kind togridwhenviewTypeis absent (:248,:359);react-page.tsx:91-93parks the author'stypeasspecType. On the objectstack side (89a156af88) the lint'sboundObjectName(validate-react-page-props.ts:885-894) accepts the canonical spelling and:1132-1135treats the required prop as satisfied by it. Renaming the four sites would publish a spelling that validates green and renders an empty list — the dev was right to stop.- Blocked-by: [finding] The #11284 convergence shipped only its producer half: the react-blocks contract deprecates
objectName/viewTypeon ListView in favour ofdata={{ provider: 'object', object }}/type, and the lint blesses that spelling — but objectui's ListView reads neither, so the canonical spelling validates green and renders an empty list #14791 — the spec-side finding (the [Decision] Doesreact-blocks.tskeepobjectName/viewTypeas sanctioned React-tier props, or do they converge on the metadata-tier spelling? — the carrier objectui#2890's last phase has never had #11284 convergence shipped only its producer half; consumer carrier objectui#2890 step 6 ispm:on-hold). Filed bare, unassigned, for the spec lane; the fork A (retract the deprecation at source) / B (land the consumer fold first) is theirs or the maintainer's, not this seat's. - Restart-when: [finding] The #11284 convergence shipped only its producer half: the react-blocks contract deprecates
objectName/viewTypeon ListView in favour ofdata={{ provider: 'object', object }}/type, and the lint blesses that spelling — but objectui's ListView reads neither, so the canonical spelling validates green and renders an empty list #14791 is ruled. A ⇒ this card closes as premise-false, nothing renamed (react-blocks.ts:167is then already correct). B ⇒ re-dispatch after objectui's fold lands, renaming the four sites plusreact-blocks.ts:167in step with it. - Correction to the seat's claim comment 5518882973: GitHub's sanitizer ate every angle-bracket JSX fragment in it, so the route-deciding sentence reads
binds a ``` there. It meant: whether a ListView block written withdata={{ provider: 'object', object: 'x' }}andtype="grid", and noobjectName/viewType`, actually binds and renders. Answered above: it does not. - The dev's three reported findings (the
typerender is silently a grid, not empty; the metadata tier'sListViewShapeSchemadeclares noobjectNameand works only because the host stamps it; the fold is hand-copied in six sibling blocks) are carried inside [finding] The #11284 convergence shipped only its producer half: the react-blocks contract deprecatesobjectName/viewTypeon ListView in favour ofdata={{ provider: 'object', object }}/type, and the lint blesses that spelling — but objectui's ListView reads neither, so the canonical spelling validates green and renders an empty list #14791's body, not filed separately.
Generated by Claude Code
- Blocked-by: [finding] The #11284 convergence shipped only its producer half: the react-blocks contract deprecates
Closed — delivered by the blocker's own landing (skills seat, session
session_01HxLw5aKDPR5RJgyUR7Exkd, half-state anchor H19 row on this card, readings at 2026-09-09T01:47Z).Blocked-by: #14791is discharged: #14791 CLOSEDcompleted2026-09-08T14:53:22Z by PR #16777 ("feat(spec)!: retire the ListView objectName / viewType react-tier aliases — data / type are the only spellings"), merged ase0af1a83onorigin/main. Re-verified on the merged ref rather than on the closure: PR #16777's changed files include exactly the four sites this card named —skills/objectstack-ui/rules/pages.mdandexamples/app-showcase/src/ui/pages/{crm-workbench,renewals-pipeline,task-desk}.page.ts— plusskills/objectstack-ui/references/react-blocks.md. Onorigin/main(419facd),git grep "ListView objectName\|viewType="overskills/objectstack-ui/**and the showcase pages returns 0 hits; positive control:rules/pages.mdcarries 5ListViewhits, so the file is readable and the zero is a reading. The "retire the deprecated spelling in one stroke" this card asked for has therefore landed, in the spec lane's PR, with the deprecation retired at its source (the #14791 fork resolved by retiring the aliases outright rather than by option A or B as written).Same write:
pm:blockedremoved (closing strips the pm-state label), no assignee.state_reason: completed— the work exists onmain; nothing for a skills-lane dev to do.
Generated by Claude Code
Close-out (consolidated seat
session_018rzQyhLGC5iVs11V3TzRs5): patrol H19 row (anchor #9857). The conversion5519011595waited on #14791's ruling with two branches (A ⇒ premise-false, B ⇒ re-dispatch after objectui's fold). The outcome was a third route: #14791 closed via merged PR #16777 (e0af1a83ed, "retire the ListView objectName / viewType react-tier aliases — data / type are the only spellings"), whose file list covers this card's whole surface —skills/objectstack-ui/rules/pages.mdand the three showcase pages.Verified on
origin/mainat 2026-09-09T01:5xZ, not taken from the closing PR's description:<ListView … objectNamehits inskills/+examples/= 0 (control:<ListViewoccurrences = 13, so the matcher fires);viewTypehits = 1, and that one is the retirement sentence inskills/objectstack-ui/references/react-blocks.md:57("theobjectName/viewTypealiases are retired"). The remainingobjectNamespellings inpages.md(:64action,:155<object-metric>,:222<ObjectForm>) are other blocks' legitimate keys, not the ListView alias this card was about. Closedcompleted, delivered by PR #16777;pm:blockedcleared.
Generated by Claude Code
Follow-up from the objectstack-ui optimization flight (#14298 / PR #14338), filed by the skills lane seat (session
session_01LraLgQVGq8egUwfYZpbYt1) on the dev's open question; the flight could not take it (a plain REWRITE outside the card's verbs, and half of it lives inexamples/**).Measured
skills/objectstack-ui/SKILL.md— the ListView prose and the react-tier example teachobjectName/viewTypeon<ListView>; this package's own generated contract (references/react-blocks.md/contracts/react-blocks.contract.json, fromgen:react-blocks) marks both DEPRECATED in favour of the canonical spelling it lists.examples/app-showcase— three example pages repeat the deprecated spelling (the flight's audit found them while measuring real usage; re-count at claim:git grep -n "objectName\|viewType" origin/main -- 'examples/**/*.page.ts' 'examples/**/pages/**'with a positive control ondefinePage).Why one card, not two
Maintainer principle (2026-08-27, verbatim): 「项目在创业阶段,用户也很少,短期不考虑渐进。」 — retired spellings retire at once, no dual-spelling grace. Fixing only the skill leaves the shipped examples teaching the other spelling until someone else notices; fixing only the examples leaves the published skill teaching a deprecated form. Both sides move in one PR so a generator reading either sees one spelling.
Scope
skills/objectstack-ui/SKILL.md— the ListView prose + the react example: canonical spelling only; net tokens ≤ 0 (rename, not addition).examples/app-showcase/src/ui/pages/*(the three pages) — mechanical rename to the canonical spelling; bounded in-place rider under the dev contract's four-condition exemption (same defect class; correct form pinned by the generated contract's DEPRECATED marker; no other claim on the files — verify at claim; same gate family). ⛔ No other example edits.skills/**md) ⇒ draft + human merge;needs:contract-reviewnot required (spelling, no semantic claim) unless the canonical form changes behaviour — verify againstpackages/spec/src/ui/page.zod.tsreact-block props.Serial: after PR #14338 merges (same file). Refs: #14292 (program) · #14298 · PR #14338.