Skip to content

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

@os-litant

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 in examples/**).

Measured

  • skills/objectstack-ui/SKILL.md — the ListView prose and the react-tier example teach objectName / viewType on <ListView>; this package's own generated contract (references/react-blocks.md / contracts/react-blocks.contract.json, from gen: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 on definePage).

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.
  • Governed (skills/** md) ⇒ draft + human merge; needs:contract-review not required (spelling, no semantic claim) unless the canonical form changes behaviour — verify against packages/spec/src/ui/page.zod.ts react-block props.

Serial: after PR #14338 merges (same file). Refs: #14292 (program) · #14298 · PR #14338.

Activity

  1. self-assigned this
    on Sep 3, 2026
  2. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Claim: skills lane seat — pm:queue → pm:dispatched and the assignee set in the same write. Dispatching one os-dev flight; the serial condition (PR #14338 merged) is long met and skills/objectstack-ui/rules/pages.md is held by no open PR (PR #14781 holds rules/list-views.md only).
    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-review on both carriers, reviewed in-seat at PASS. Governed skills/** ⇒ draft PR; review requests are the seat's step.

    Re-measured at origin/main df657d9df, 2026-09-03T01:19Z — the card's premise moved:

    • viewType occurs 0 times in the published ui prose (skills/objectstack-ui/**/*.md excluding the generated contract); the flights since the card removed it. <ListView objectName=…> survives at rules/pages.md:180 (prose) and :218 (the react example) — SKILL.md itself 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 only viewType="grid" left) and :169, task-desk.page.ts:44. <ObjectForm objectName> / <ObjectChart objectName> on the same pages are not deprecated (references/react-blocks.md:88 carries no marker) and stay.
    • The generated contract's rows: references/react-blocks.md:61 objectName → [DEPRECATED → data={{ provider: 'object', object }}], :62 viewType → [DEPRECATED → type].
    • ⚠️ Two source facts that decide the route, for the dev to settle FIRST: (1) the spec's own REACT_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) objectui packages/core/src/utils/normalize-list-view.ts folds fields → columns and canonicalises viewType, but shows no fold from data.object into objectName, while packages/plugin-list/src/ListView.tsx:791 / :978 still read schema.objectName. Whether <ListView data={{ provider: 'object', object: 'x' }} type="grid"> with no objectName actually binds is the fact. Route 1 (it binds at source): rename in rules/pages.md and the three pages, Clause-② as above. Route 2 (it does not): the DEPRECATED marker is the false claim, the flight stops with needs_decision and 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.md is 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

  3. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Claim: dev seat (os-dev subagent of the dispatching PM session — same session id, distinct seat)
    Session: session_01LraLgQVGq8egUwfYZpbYt1
    Branch: none created
    Worktree: none created

    The 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 ListView at objectui 42c129b6. Route 2 forbids the rename, so no branch, no worktree, no edit, no PR. Both read-only checkouts are byte-clean (git status --porcelain empty 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-report comment that follows.


    Generated by Claude Code

  4. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    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

    ListView binds its object through schema.objectName and nothing else. Both of its effects early-return on a falsy objectName (ListView.tsx:1405, :1684) and the single query call is dataSource.find(schema.objectName, …) (:1978); the file has zero reads of data.object in 4,132 lines, and normalizeListViewSchema folds ten documented pairs, none of them this one. The visualization half never even arrives: on a react page the wrapper hands the type slot to the SDUI discriminator and parks the author's value under specType (react-page.tsx:91-93), and specType is read by exactly one block in the repo — plugin-charts — not this one. With viewType then absent, normalize-list-view.ts:248/:359 force it to 'grid'.

    The sharpest part is not the missing fold, it is that os validate is 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 dashboard utils.ts) each carry the data.object fold by hand — so the fold is a per-block duty, and ListView simply never took it. git grep 11284 across objectui packages/: 0 hits.

    Nothing was renamed. No branch, no worktree, no PR; git status --porcelain is empty in both checkouts.


    Generated by Claude Code

  5. os-litant commented on Sep 3, 2026

    @os-litant
    CollaboratorAuthor

    Route 2 — pm:dispatched → pm:blocked, same write; assignee kept. Skills-lane seat (session session_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:1405 and :1684 early-return on a falsy schema.objectName, :1978 is dataSource.find(schema.objectName, …), and the file has 0 reads of data.object, specType or provider === 'object'; normalize-list-view.ts has 0 .object and forces the kind to grid when viewType is absent (:248, :359); react-page.tsx:91-93 parks the author's type as specType. On the objectstack side (89a156af88) the lint's boundObjectName (validate-react-page-props.ts:885-894) accepts the canonical spelling and :1132-1135 treats 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.


    Generated by Claude Code

  6. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Collaborator

    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: #14791 is discharged: #14791 CLOSED completed 2026-09-08T14:53:22Z by PR #16777 ("feat(spec)!: retire the ListView objectName / viewType react-tier aliases — data / type are the only spellings"), merged as e0af1a83 on origin/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.md and examples/app-showcase/src/ui/pages/{crm-workbench,renewals-pipeline,task-desk}.page.ts — plus skills/objectstack-ui/references/react-blocks.md. On origin/main (419facd), git grep "ListView objectName\|viewType=" over skills/objectstack-ui/** and the showcase pages returns 0 hits; positive control: rules/pages.md carries 5 ListView hits, 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:blocked removed (closing strips the pm-state label), no assignee. state_reason: completed — the work exists on main; nothing for a skills-lane dev to do.


    Generated by Claude Code

  7. yinlianghui commented on Sep 9, 2026

    @yinlianghui
    Collaborator

    Close-out (consolidated seat session_018rzQyhLGC5iVs11V3TzRs5): patrol H19 row (anchor #9857). The conversion 5519011595 waited 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.md and the three showcase pages.

    Verified on origin/main at 2026-09-09T01:5xZ, not taken from the closing PR's description: <ListView … objectName hits in skills/ + examples/ = 0 (control: <ListView occurrences = 13, so the matcher fires); viewType hits = 1, and that one is the retirement sentence in skills/objectstack-ui/references/react-blocks.md:57 ("the objectName / viewType aliases are retired"). The remaining objectName spellings in pages.md (:64 action, :155 <object-metric>, :222 <ObjectForm>) are other blocks' legitimate keys, not the ListView alias this card was about. Closed completed, delivered by PR #16777; pm:blocked cleared.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions