Skip to content

docs(skills): stageOrder is funnel-only at the three hand-written sites - #17538

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-17471-stageorder-funnel-only
Sep 11, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/issue-17471-stageorder-funnel-only

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes #17471

What

Three hand-written sites documented options.stageOrder for a funnel / pyramid pair. There is no pyramid widget type: it was removed from ChartTypeSchema as a variant that only ever rendered as funnel (taxonomy NOTE at the foot of packages/spec/src/ui/chart.zod.ts; packages/spec/src/ui/chart.test.ts pins the refusal alongside its fallback-only siblings). An author copying type: 'pyramid' out of the skill table got a parse refusal.

Each site now carries the two statements parent #17344's schema PR #17474 put into dashboard.zod.ts (carried, not re-derived): (1) explicit stage order for a funnel widget, as the dimension's stored values; (2) funnel is the only widget type that reads the key — on any other type it parses and is never consulted (those order with sortBy / sortOrder). Same facts, each in its site's own register. Net 0 lines at all three sites; the schema and the generated reference page are untouched (the parent's).

site before after
skills/objectstack-ui/rules/dashboards.md:345 (ratcheted, row 183 → 185 bytes, +2) Explicit category order for funnel/pyramid. Stored values, not display labels; omit it to inherit the field's own picklist order. Explicit stage order for funnel; no other type reads it. Stored values, not display labels; omit to inherit the field's picklist order.
content/docs/ui/dashboards.mdx:121 (row 196 → 342 bytes; no ratchet) Explicit stage order for funnel/pyramid, as the dimension's **stored values**. Omit to use the dimension field's picklist option order. Explicit stage order for a funnelwidget, as the dimension's **stored values**.funnelis the only widget type that reads it — on any other type the key parses and is never consulted; order those withsortBy/sortOrder. Omit to use the dimension field's picklist option order.
packages/sdui-parser/src/dashboard-widget-options.ts:51 (row 77 → 101 bytes) (funnel/pyramid stage order) (funnel stage order — the only type that reads it)

Budget on the ratcheted file: file 24356 → 24358 bytes, ceil(bytes / 4) 6089 → 6090 against ceiling 6090 (headroom 1 → 0). The +2 bytes are paid inside the same row: categorystage (−3, and the parent's own word), omit itomit (−3), field's ownfield's (−4); no ceiling raise, no payment from another row.

Changeset: .changeset/sdui-parser-stageorder-funnel-only.md, @objectstack/sdui-parser patch. Measured rather than assumed: files[] ships dist, and after pnpm --filter @objectstack/sdui-parser run build the new sentence appears in dist/index.d.ts and dist/index.d.mts (positive control stageOrder hits index.js / index.mjs / both d.ts; the old funnel/pyramid text: 0 hits), so the tarball moves and skip-changeset does not apply. Comment-only ⇒ patch.

Premise check (measured on this branch, spec built from efa2533d sources)

(a) ChartTypeSchema.safeParse through the built package (import.meta.resolve('@objectstack/spec/ui')packages/spec/dist/ui/index.mjs, not src/):

ACCEPT  "funnel"        LIT
ACCEPT  "bar"           LIT
REFUSE  "pyramid"       the type the three sites name
REFUSE  "ziggurat"      DARK
REFUSE  "bi-polar-bar"  DARK
options.stageOrder parses: ACCEPT

Premise holds (premise_still_valid: true).

(b) grep -n pyramid on the three files at efa2533d: exactly one hit each (dashboards.md:345, dashboards.mdx:121, dashboard-widget-options.ts:51), none elsewhere in those files. After the edit grep -c pyramid = 0 on all three; line counts unchanged (462 / 553 / 193).

(c) Lockstep: scripts/check-sdui-lockstep.mjs fingerprints three things — the .objectui-sha pin against recordedAgainstPin, the grammar region of packages/sdui-parser/src/parse.ts (delimiter to EOF, git blob id) and the diagnostic-code set extracted from the AST (code: properties and error(...) first arguments). A comment line in dashboard-widget-options.ts's header reaches none of them, and the file's own header names the header as "the one deliberate divergence" from objectui's copy (the byte-equal region starts at the import line). So the line was edited here. Gate before and after the edit, identical verdict: check:sdui-lockstep: OK — this copy is byte-identical to objectui@53ded82bf7a4 (2026-09-05T15:42:42+00:00) over 214 grammar line(s) [blob 0131f27cf86d] and agrees on all 24 diagnostic code(s), across 7 non-test source(s). (exit 0 both times). objectui's twin at the pin (53ded82b, read through the REST contents endpoint) carries the same (funnel/pyramid stage order) line at its :25 — objectui's, see Acceptance notes.

(d) check-skills-token-ratchet before: skills/objectstack-ui/rules/dashboards.md is 6089 tokens (ceiling 6090; headroom 1), bundle total 139496. After (head 6a854fe9): skills/objectstack-ui/rules/dashboards.md is 6090 tokens (ceiling 6090; headroom 0), bundle total 139497; 34 authored bundle file(s) within their ceilings — exit 0 both times.

The one judgement — how much of the parent's second statement each site carries, on the four axes

The skill row keeps the fact (no other type reads it) and drops the routing hint (sortBy / sortOrder), while the docs row and the code comment carry the statement in full. 实际业务需求: the fact is what stops an author putting stageOrder on a bar widget and reading the unchanged order as a bug; the routing hint is already the sortBy row three lines up in the same table, so in the skill it buys nothing an author does not have in view, and the measured budget on that file is 4 bytes. 项目长远合理性: the corpus and the schema say the same two things; the skill row is the compressed form, the docs row the expository form, the schema prose the authority — one contract, three registers, no fork. 防 AI 写元数据犯错: the refusal side is already structural (ChartTypeSchema refuses pyramid at parse, pinned); the silent side — the key parsing on a type that never reads it — is exactly what the sentence no other type reads it guards in the corpus AI authors from, so it is the sentence that survives the budget. 创业阶段不扩散需求: no new row, no new example, no ceiling raise; the ratchet's +2 bytes is paid by deleting words in the same row, which is the only currency the skills/** rule accepts.

Verification

Local = targeted gates; the farm is CI. Exit codes captured before any pipe (cmd > log 2>&1; EXIT=$?); verdict lines quoted from the gates' own output.

  • dispatch-gates.mjs --commands derived on a9a2f0ba (83 commands) and again on the final head 6a854fe9 (90; the 7 added are the changeset families). Every command was run and recorded as command :: exit N; dispatch-gates.mjs --ran on 6a854fe9: ✓ dispatch-gates --ran: 90 derived famil(ies) accounted for — 89 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3). (exit 0).
  • NOT MEASURED: pnpm check:dual-build-cjs-loads — exit 3 PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ (a repo-wide build exceeds the foreground cap on the shared box). Declared narrowing: the diff's only source change is a header comment whose emitted JS is byte-identical (funnel/pyramid 0 hits and the new sentence 0 hits in dist/index.js / index.mjs after rebuild — it reaches only the d.ts), so this gate's inputs did not move; CI owns the measurement.
  • Rerun after their prerequisites were built (@objectstack/formula, @objectstack/lint, @objectstack/client-react closures under the verify lock, 318 s): check:doc-formula-expressions exit 0 (✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 438 files / 1375 TS blocks judged clean), check:doc-security-posture exit 0 (✅ 27 ObjectSchema.create example(s) in 227 marked block(s) across 239 prose file(s) in 2 root(s) carry an os validate-clean security posture), check:skill-examples exit 0 (✅ 258 prose examples type-check across 3 surface(s)); check-plugin-teardown-shape.mjs --self-test exit 0 after git fetch --depth=1 of its pinned positive-control commit 621a4876 (first run refused on the shallow clone — a prerequisite, not a red).
  • Named families, both before the edit and on the final head, all exit 0: check-skills-token-ratchet (quoted above); check:sdui-lockstep (quoted above); check:doc-authoring (✓ doc authoring guard: 401 files clean — no bare metadata literals. / 44 published skill files clean); check:nul-bytes (check-nul-bytes: OK (scanned 8319 text file(s) … no raw ASCII control bytes)) plus a grep -naP control-character self-scan on the three files, 0 hits; check-changeset-no-major.mjs --base origin/main, check-adr-0087-registration.mjs --base origin/main, check-empty-changeset.mjs --base origin/main, check:changeset-gate-self-tests, check:objectui-changeset, check:pm-changeset-deadline-census, check:published-files, check:widget-option-census, check:pm-governed-merges.
  • Affected published package: pnpm --filter @objectstack/sdui-parser run typecheck exit 0 (tsc --noEmit, script name echoed) and pnpm --filter @objectstack/sdui-parser run test exit 0 — Test Files 7 passed (7), Tests 138 passed (138) — under os-verify-lock.sh (VERDICT lines read, not bare $?). No public surface moves, so no consumer sweep is owed.
  • Path face: node scripts/pm/check-governed-merges.mjs --branch claude/issue-17471-stageorder-funnel-only on 6a854fe9: exit 3, ⛔ GOVERNED — a human merge is the review record for this PR, skills/** ×1 — skills/objectstack-ui/rules/dashboards.md; paths not on the register: the changeset, dashboards.mdx, dashboard-widget-options.ts.
  • Model tier: dispatch-gates.mjs --tier on the three paths prints MANDATORY (published skills/**, clause ①).

Clause-②: no — prose at three sites plus a changeset; no accept set or public surface moves (stageOrder parses exactly as before, shown by the probe's last line).

Acceptance notes

  • noted, not filed: objectui's packages/sdui-parser/src/dashboard-widget-options.ts at the pin 53ded82b carries the same (funnel/pyramid stage order) comment at :25. It is in objectui's header, which the lockstep gate deliberately does not compare, so nothing here goes red; it is a code comment, not an authoring example, so it is outside the three filed classes. 承接者: the next objectui-side port of this header (whoever runs pnpm gen:sdui-lockstep after a pin bump reads both headers side by side).
  • noted, not filed: check:dual-build-cjs-loads cannot be measured on a partially built worktree (exit 3 prerequisite); nothing about the gate is wrong — its refusal is loud and names the remedy. 承接者: none.
  • The dispatch's conditional on the sdui-parser changeset resolved by measurement (comment reaches the shipped d.ts ⇒ patch changeset), consistent with both the dispatch and the skip-changeset rule.

维护者速读(草稿)

改了什么:三处手写文档里 options.stageOrder 的说明从「funnel / pyramid」改为只说 funnel,并加一句「只有 funnel 读这个键」——技能规则表一行、文档页表格一行、sdui-parser 头注释一行,行数净零;sdui-parser 附一条 patch changeset(注释会进发布的 d.ts)。

为什么改:pyramid 不是图表类型,schema 直接拒收;作者(人或 AI)照抄技能表里的 pyramid 会得到一次解析失败。父卡 #17344 已把 schema 的说明改对,这三处是 schema 之外的残留,改成与 schema 同一口径。

风险与代价(含回滚):纯文案,不动接受集、不动运行时;dashboards.md 的 token 预算刚好贴顶(6090/6090),多出的 2 字节在同一行内删词支付。回滚即 revert 两个 commit,无数据或迁移影响。

席位意见:(留空,席位定稿)

你要做的:确认技能表那一行的压缩措辞可接受(删掉了 sortBy 的指引,因为同表上方三行就是 sortBy 行),然后人工合并;本 PR 保持 draft,不进队列。


Generated by Claude Code

os-litant and others added 2 commits September 10, 2026 19:59
The dashboards skill rule table, the dashboards docs page and the
sdui-parser header comment still documented `options.stageOrder` for a
`funnel` / `pyramid` pair. `pyramid` is not a chart type: it was removed
from `ChartTypeSchema` as a variant that only ever rendered as `funnel`,
and `chart.test.ts` pins the refusal. Each site now carries the two
statements the schema prose already makes: explicit stage order for a
funnel widget, as the dimension's stored values, and `funnel` is the only
widget type that reads the key.

Net 0 lines; the ratcheted skill row grows by 2 bytes, paid inside the
row (`category` -> `stage`, `omit it` -> `omit`, `field's own` ->
`field's`).

Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK
Co-authored-by: Claude <noreply@anthropic.com>
…nt fix

The edited header comment is carried into the published `index.d.ts`
(measured: the new sentence appears in `dist/index.d.ts` and
`dist/index.d.mts`, and `files[]` ships `dist`), so the tarball moves and
a patch changeset is owed; `skip-changeset` does not apply.

Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/sdui-parser/src/dashboard-widget-options.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/sdui-parser/src/dashboard-widget-options.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json edaf3b2f7832805d4d7c544fb88d696ff8056924packageMentionDocs.

Copy link
Copy Markdown
Collaborator Author

维护者速读

改了什么 —— 三处手写文档里 options.stageOrder 的说明从「funnel / pyramid」改成只说 funnel,并带上 schema 已落地的第二句「只有 funnel 读这个键」:技能规则表一行(skills/objectstack-ui/rules/dashboards.md,183 → 185 字节,多出的 2 字节在同一行内删词支付,文件 6090/6090 token 贴顶不抬)、文档页表格一行(content/docs/ui/dashboards.mdx)、sdui-parser 头注释一行;行数净零。sdui-parser 附一条 patch changeset,因为那行注释会进发布的 index.d.ts(dev 实测,emitted JS 不变)。

为什么改 —— pyramid 不是图表类型,schema 直接拒收(chart.test.ts 钉着);作者(人或 AI)照抄技能表里的 pyramid 会得到一次解析失败。父卡 #17344 的 PR #17474 已把 schema 自己的说明改对,这三处是 schema 之外的残留,现在与 schema 同一口径。

风险与代价(含回滚) —— 纯文案,不动接受集、不动运行时;check:sdui-lockstep 前后都绿(该门禁不比对头注释,文件头也写明头注释是刻意分叉处)。回滚即 revert 这两个 commit。CI 在 head 6a854fe9 上 11 过 / 2 跳 / 18 在跑,无失败;本席在 head 上实测 token 棘轮 6090/6090 exit 0、三处 pyramid 归零。

席位意见 —— 通过。技能表那行的压缩措辞(删掉 sortBy 指引,因为同表上方三行就是 sortBy 行)本席认可。受管面(skills/**,对外发布):本席不翻 ready、不入队、不批准。

你要做的 —— 一个动作:人工合并本 draft PR。

skills 席,session session_01YKEjmbYNvYWJvWGSWx26zK,2026-09-10T20:26Z。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 11, 2026 06:29
@os-zhuang
os-zhuang enabled auto-merge September 11, 2026 06:29
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit f55922f Sep 11, 2026
39 of 40 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-17471-stageorder-funnel-only branch September 11, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Three hand-written sites still document options.stageOrder for a pyramid chart type the schema refuses

2 participants