docs(skills): navigation.md states the measured behaviour of colSpan and span: 'full', drops the deprecated claim - #18674
Conversation
…ure as, drops the deprecated claim The Field width bullet in skills/objectstack-ui/rules/navigation.md prescribed span: 'full' as the whole row and called colSpan deprecated and misaligned at every width but one. Browser measurement at three surface widths found the opposite: colSpan is clamped to the current column count and grid-aligned at every width, while span: 'full' is emitted by the pinned renderer at the widest tier only. Nothing in packages/spec deprecates colSpan. The bullet now states both keys as packages/spec declares them, with the tier caveat the spec anchors to the objectui pin. Two restatements in the same section are deleted to hold the token ceiling: the overlay bullet's reason clause (already the section intro's) and the column bullet's closing clause (already its first sentence). Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu Co-authored-by: Claude <noreply@anthropic.com>
维护者速读(终稿)skills 席 改了什么 — 只改发布技能 为什么改 — #17328 实测把这两条主张判为假,PR #18492 已从 风险与代价(含回滚) — 受管发布面,只改文本;不动 spec、不动示例、不抬棘轮。新句里「渲染器只在最宽档位输出」锚在当前 席位意见 — 荐合。它把规则改成 spec 和测量说的话,不是把旧句反过来;偏好被移除而非反转,与 PR #18492 一致。 你要做的(一个动作) — Approve 本 PR(或直接人工合并);席位随后按裁定 C 落地。若你希望技能明确偏好某一个键,Request changes 并说哪个,本席另起。 Generated by Claude Code |
Fixes #18496
Clause-②: no
One bullet in one published rule file.
skills/objectstack-ui/rules/navigation.md(the 「Field width」 bullet,:136–:139onorigin/main30be2ac0b) prescribedspan: 'full'as "the whole row" and told authors not to usecolSpanbecause "it only lines up at one width and is deprecated". #17328 measured both rendering claims false in Chromium at 390 / 720 / 1700, PR #18492 removed them frompackages/specand the generated reference page, and nothing inpackages/specdeprecatescolSpan. This was the last prescriptive carrier in the tree, and the onenpx skills add objectstack-ai/objectstack/skillsships to customer projects. The bullet now says what the spec declares and the measurement found; it prescribes no preference between the two keys, because the spec does not (PR #18492: 「The preference between the two keys is removed, not reversed」).The bullet, before and after
Before (
origin/main30be2ac0b,:136–:139):After (
7155de6ac,:136–:141):Two restatements in the same section are deleted to pay the token ratchet (below); no other line moves:
:142–:143→:144: the parenthetical 「(they can't be chosen without knowing the client viewport)」 is gone. It restated the section intro:127–:128「because an author (especially an AI) cannot know the client's screen」. The 「width/drawerWidthare deprecated」 clause it hung on stays, and it is true:packages/spec/src/ui/view.zod.ts:3539–:3540carries@deprecatedondrawerWidthand the describe opens[DEPRECATED → size buckets].:146–:147→:147: the closing 「; the columns adapt themselves」 is gone. It restated the bullet's own first sentence, 「Not authored. The form grid follows its real rendered width via container queries」.What the spec declares —
packages/spec/src/ui/view.zod.tsat30be2ac0b:2664colSpan: z.number().int().min(1).max(4).optional(), describe: 「Absolute column span (1-4). The renderer clamps it to the form grid's current column count, so the cell starts at a real column boundary at every surface width and never overflows (colSpan: 4in a 3-column grid renders as 3); acolSpanwithin the column count renders as authored, andcolSpan: 1emits no span class at all.」:2676span: z.enum(['auto', 'full']).default('auto'), describe: 「'auto' (default — omit it): the renderer sizes the field from its widget type × the current column count (wide widgets like textarea/richtext/json/file/subform take the whole row). 'full': resolves to the form grid's full column count. How far down the container-query tiers that span is emitted is the renderer's, not this key's: at the.objectui-shapin53ded82bf7only the widest tier's class is emitted (@2xl:col-span-3for a 3-column grid), so at intermediate widths the field took a single cell, not the row (one of two at the 720px modal width; measured in Chromium at viewport widths 390, 720 and 1700).」:2665–:2675the TSDoc abovespan: objectui#9253 (objectuibd09957380, 2026-09-12) is 「ahead of that pin」 and the block says 「re-read this block at the pin bump that absorbs it」.colSpan, measured overpackages/spec/src:.deprecated(→ 0 files;retiredKey(calls namingcolSpan→ 0;UNKNOWN_KEY_GUIDANCE(7 files) andRETIRED_KEY_GUIDANCE(8 files) → 0colSpanlines inside them; the ADR-0087 registriespackages/spec/src/conversions/*andpackages/spec/src/migrations/*→ 0colSpanlines (positive control:namespace/tableName/sourceViewhitconversions/registry.ts×3,apply.ts×2,types.ts×2,stored.ts×1);colSpanlines co-occurring withdeprecat|retire|legacy→ 0. The changeset PR docs(spec,ui): colSpan and span state their measured behaviour — the two claims #17328 falsified are gone #18492 landed (.changeset/17670-colspan-span-measured-behaviour.md) states 「Nothing an author writes moves. Both keys are unchanged, both still parse」.content/docs/references/ui/view.mdx:201–:202andcontent/docs/ui/views.mdx:449–:450say the same two sentences;content/docs/protocol/objectui/layout-dsl.mdx:254–:262(「Custom Span Widths」) says 「widest tier only at objectui53ded82bf7… one clamped class per tier since objectui#9253」.What the measurement found — #17328, Chromium at 390 / 720 / 1700
From the card's geometry table (hotcrm at
427c98535d,@objectstack/*17.4.0, one 3-column section,helpful_countcell):colSpan: 2@md:col-span-2span: 'full'@2xl:col-span-3colSpan: 4@2xl:col-span-3(identical tospan: 'full')colSpan: 1Overflow 0px in every configuration at every width. So:
colSpanis clamped and grid-aligned at every width (the old bullet's 「only lines up at one width」 is contradicted at all three), andspan: 'full'at the pinned renderer is one cell of two at the modal width (the old bullet's 「the whole row」 is false there). The bullet's parenthetical 「a 3-column form: one cell of two in a modal」 is that row; it is qualified to a 3-column form because a 2-column form grid resolves'full'to 2 and emits@md:col-span-2, which is the row at 720.Is the pin state still the shipped state on
30be2ac0b?.objectui-sha=53ded82bf7a494f54e344e19099dbf00854b8694, untouched since PR #18492 merged (git log 3a9ad22ecf..HEAD -- .objectui-shais empty). In the objectui clone (git rev-parse --is-shallow-repository= false):git merge-base --is-ancestor bd09957380 53ded82bf7→ exit 1; control legPIN~5(83c77dc30, 2026-09-05) → exit 0; reverseis-ancestor 53ded82bf7 bd09957380→ exit 0;git tag --contains bd09957380→ 0 tags. The span-ladder fix is ahead of the pin and unreleased, so 「the shipped renderer emits it at the widest tier only」 is true today. It stops being true at the pin bump that absorbsbd09957380— the same event the spec TSDoc:2675tells its own reader to re-read at; see acceptance note 3.Why no preference is prescribed
The dispatch left the direction to the measurement: 「If the measurement says neither spelling is to be discouraged, say so; if it says
colSpanis the safer primitive, say that」. At the pin,colSpanis the one that does what it says at every width, and a rule could say so — but the spec (the protocol, per 「我们的项目以objectstack 协议为准,文档应该以实际实现为准」) declares both keys without preference after PR #18492's at-tier contract review, and the renderer half is already fixed ahead of the pin (objectui#9253), after which'full'is the whole row at every multi-column tier and acolSpan-first rule would need reversing again. So the bullet states each key's declared behaviour and the measured caveat, and lets the author choose; it calls neither deprecated because the spec does not.Token ratchet —
scripts/check-skills-token-ratchet.mjs:472['skills/objectstack-ui/rules/navigation.md', 2273], unchangedConvention
ceil(utf8 bytes / 4). Before: 9092 bytes → 2273 tokens (ceiling 2273, headroom 0). After (7155de6ac): 9091 bytes → 2273 tokens (ceiling 2273; headroom 0) — the gate's own line: 「✓ check-skills-token-ratchet: skills/objectstack-ui/rules/navigation.md is 2273 tokens (ceiling 2273; headroom 0).」 Arithmetic: bullet 319 → 411 bytes (+92); the two deletions −61 and −32 (−93); net −1 byte. Row not raised, not lowered. File 162 → 162 lines; widest line in the section 84 (file-wide 181, a code example, unchanged).Sweep —
git grep -z -l -F, excludingcontent/docs/releases/and**/CHANGELOG.md, files30be2ac0b)7155de6ac)only lines up atskills/objectstack-ui/rules/navigation.md,.changeset/17670-colspan-span-measured-behaviour.mdat any column countlint-discouragedexamples/app-showcase/src/ui/views/task.view.ts(carrier 2, boundary)is deprecatednavigation.md1 linenavigation.md0colSpanandis deprecatedview.zod.ts,references/ui/view.mdx,navigation.mdview.zod.ts,view.mdx(there 「is deprecated」 is on other keys; 0 same-line hits withcolSpan)colSpanzzz-no-such-tokenThe changeset carries the two old phrases as quotations of the wording it removed — a historical record by construction, consumed into
CHANGELOG.mdat release (which the exclusion set already exempts); it is not a prescription and is not touched.Gates (worktree at
7155de6ac)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack— change set derived from git, 1 path vs merge base30be2ac0b— derived 22 commands; every one run with redirect-then-$?capture;--ranreconcile: 「22 derived, 22 run, 0 NOT-MEASURED, 0 UNRUN」, exit 0.Named verdicts:
check:doc-authoring「44 published skill files clean — no internal issue-id references」;check-skills-token-ratchet --self-test「65 cases pass」;check:skill-identifier-liveness --self-test OKand the gate exit 0;check:skill-compatibility「10 SKILL.md file(s) reconciled against 81 workspace packages」;check:nul-bytesexit 0.check:doc-formula-expressionswas run afterpnpm --filter '@objectstack/lint...' buildunderscripts/pm/os-verify-lock.sh(「VERDICT command-exit 0 · held the lock 186s · waited 0s」). Not owed locally: no ① dependency-closure build and no ② package test (the diff touches no package); the 51 artifact-roster, 11 wide-population and 2 workflow-valued families are CI's own.Governed:
node scripts/pm/check-governed-merges.mjs --test skills/objectstack-ui/rules/navigation.md→ exit 3, 「GOVERNED — a human merge is the review record for this PR … skills/** ×1 — the published skills catalog」. Recorded as GOVERNED, not as a failure. Draft; no seat flips it ready, queues it or arms auto-merge; the maintainer's approval lands it (Prime Directive #14).Control characters:
grep -naPover the touched file → no match (exit 1).Changeset —
skip-changeset, by measurement83 tracked
package.jsonmanifests read: 0 carry afiles[]entry naming askillspath (positive control: 70 namedist);skills/*/package.json→ 0; the catalog ships from the GitHub tree (skills/README.md:9npx skills add objectstack-ai/objectstack/skills;packages/create-objectstack/src/skills-install.ts:62SKILLS_CATALOG = 'objectstack-ai/objectstack/skills'), never inside an npm tarball;.github/workflows/pr-automation.yml:799namesskills/**among the releases-nothing paths.Acceptance notes
Out of this PR's scope; nothing is filed by this PR.
examples/app-showcase/src/ui/views/task.view.ts:363–:366(「'full' = whole row at any / derived column count — the primary primitive; absolute colSpan / is legacy and lint-discouraged」, wrapped across comment lines; the lint rule it names was withdrawn by [finding] absolute-colspan-discouraged asserts a misalignment that does not happen — measured in Chromium at all three surface widths, and its recommended alternative span: 'full' is the spelling that misrenders #17328). skills/objectstack-ui/rules/navigation.md still prescribes span over colSpan on the basis #17328 measured false, and calls colSpan deprecated when nothing in packages/spec deprecates it #18496 records it as a boundary, not an expansion; left exactly as found, and the sweep row above keeps it visible.bd09957380(PR docs(spec,ui): colSpan and span state their measured behaviour — the two claims #17328 falsified are gone #18492's acceptance note 5 names the same event; dedupe wordsspanLadderFor,objectui#9253,span-full-tier-ladder). The new bullet's 「the shipped renderer emits it at the widest tier only」 is anchored to the current pin the way the spec describe is, but no gate watchesskills/**for pin-state prose (packages/spec/scripts/check-objectui-pin-citations.tsscanspackages/spec/srconly), so that PR must re-readnavigation.md:139–:141alongsideview.zod.ts:2665–:2676.content/docs/protocol/objectui/layout-dsl.mdx:850lists aFormFieldinterface withcolSpanand nospan— incompleteness, not an error; the card's own refs exclude it..changeset/17670-colspan-span-measured-behaviour.mdstill carries 「only lines up at」 and 「at any column count」 as quotations of the removed spec wording; release consumes it intoCHANGELOG.md, where the exclusion set already expects them.维护者速读(草稿)
改了什么:
skills/objectstack-ui/rules/navigation.md「Record Presentation」一节的「Field width」要点重写:不再规定「用span: 'full'占满整行、别用colSpan,它只在一种宽度对齐且已弃用」,改为按packages/spec的声明陈述两个键——默认省略(auto);两个键都合法;colSpan(1–4)被渲染器夹到当前列数、在每个宽度都对齐网格;span: 'full'解析为表单网格的满列数,但当前出货的渲染器只在最宽一档发出它(3 列表单在弹窗里只占两格中的一格)。为了守住 token 棘轮(该文件上限 2273、余量 0),同一节删掉两处重复表述:「Overlay width」里的括号原因句(节首已说过同一理由)和「Column count」末尾的「the columns adapt themselves」(该要点首句已说)。棘轮行不动,实测仍是 2273。为什么改:#17328 在 Chromium 三个宽度实测:
colSpan被夹紧、每个宽度都对齐;而被推荐的span: 'full'在弹窗宽度只占一格、与不写一样。PR #18492 已把这两句从 spec 与生成的参考页移掉,并明确「两键之间的偏好是移除、不是反转」。这一条是树里最后一处仍在规定旧说法的载体,而且随npx skills add出货到客户项目;「deprecated」一句更是独立于实测就是假的——spec 里没有任何东西弃用colSpan(.deprecated()0、retiredKey0、ADR-0087 转换表 0、UNKNOWN_KEY_GUIDANCE0)。风险与代价(含回滚):① 新要点的「当前出货的渲染器只在最宽一档发出」锚定在
.objectui-sha53ded82bf7(objectui 修复bd09957380在 pin 之后、尚未发布,双腿实测);pin 升级吸收该修复时这句要跟着重读,spec 的 TSDoc 对自己也写了同一句提醒,但skills/**没有门禁盯 pin 状态的散文——已记在验收备注 2,承接者是 pin 升级 PR。② 不再给作者一个「首选拼法」,由作者按两句事实自选;这与 spec 现状一致。③ 同节另两处删除只删重复表述,不删事实。回滚 = revert 单个提交(单文件,无发布物,无 changeset)。席位意见:
你要做的(一个动作):批准并合并本 PR(受管面
skills/**,需你点合并)。Generated by Claude Code