Repository navigation
docs(skills): the objectui action-block rule teaches variant 'danger' for a destructive action; destructive is the Button primitive's word - #27588
Conversation
… for a destructive action; destructive is the Button primitive's word The "Actions Are Node Types" rule in skills/objectui/rules/protocol.md gains one line: "variant": "danger" draws the action block red and asks its confirmText question through the destructive confirm button; destructive is the Button primitive's word (variant="destructive" in React) and is refused on an action block at save, naming the rename. Paid in the same file under the skills token ratchet (4357 -> 4366 of 4368): a provenance sentence about the retired dataSource measurement route and a sentence restating the props-envelope rule, both stated elsewhere in the file, are deleted. One patch changeset for @objectstack/skills. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
…ion block and expects variant 'danger' The first case in the bundle to exercise an action block's variant: a 'Delete product' button that must read as destructive and confirm first. The ideal answer writes "variant": "danger" with a confirmText on an action:button; "variant": "destructive" is a must_not_contain, spelled as the JSON key-value so an answer naming the Button primitive's word still passes. Paid down first: the three existing prompts and rubrics tightened, every must_contain token and fact kept (1206 -> 914 prose bytes). The CEILINGS row for skills/objectui/evals/page-builder.json moves 560 -> 684, pinned at the measurement (2238 -> 2735 bytes), under the maintainer ruling of 2026-10-27 quoted verbatim in the row's comment. No other row moves. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
…action eval and its ceiling move Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU Contract reviewServed-tier: Inputs read:
The rest of the diff (the changeset and ① Derived judgments
② Semver level
Should-fix (non-blocking, prose outside ③ Boundary flags
④ Design judgment
No must-fix. Should-fixes: the stale PR body (③) and the stale changeset body (②), both prose outside Implemented-by: VERDICT: PASS |
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU Contract reviewServed-tier: Delta review of ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU 席位核对 — ACCEPT · #27243 · head
|
Part of #27243 — this PR lands the teaching line and the changeset. The eval case the card also wants cannot be paid under
skills/objectui/evals/page-builder.json's ceiling by honest deletion (560 tokens, headroom 0; the case costs 124 tokens after every tightening), and the claim forbids aCEILINGSraise, so it waits for the seat's decision — Acceptance notes.Clause-②: no
Metadata-protocol: none
维护者速读
rules/protocol.md的「Actions Are Node Types」规则新增一句:"variant": "danger"让动作块变红并经由 destructive 确认按钮提问;destructive是 Button 原语的词,写在动作块上保存时被拒。同文件删去两句他处已讲的话付账。一条@objectstack/skillspatch changeset。danger, the word ActionSchema.variant uses #26966 把动作块的variant从destructive改名danger,但技能包没教过动作块的variant;AI 作者只能从 React 示例里抄到destructive,写到动作块上即被保存拒收。rules/protocol.md4357 → 4366 / 4368,上限不动;回滚即 revert 本 PR。skills/objectui/rules/protocol.md(新增段落 L275–L278;删去的两句原在 L139–L141 与 L161–L163,均为同文件他处已陈述的事实)。node scripts/check-skills-token-ratchet.mjs,预期见skills/objectui/rules/protocol.md 4366 / 4368 (-2)且末行为绿勾。page-builder.json的上限抬 +124,或改走别的路;Tier H 技能面,席位复核后由维护者 APPROVED 落地。What changes
skills/objectui/rules/protocol.md, rule "Actions Are Node Types, Not An Event Bag": one paragraph after theaction:buttonexample."variant": "danger"draws the button red and asks itsconfirmTextquestion through the destructive confirm button;destructiveis the Button primitive's word (variant="destructive"on the React Button, whichguides/auth-permissions.mdL250 andrules/composition.mdL143 already show); on an action block the spec refuses it at save, naming the rename.dataSourcewiring this was first measured through is retired; the envelope readings below are unchanged by that" (the retirement is taught under "Rule: Data Binding Path Resolution", now L220–L229), and the closing restatement "What is not open: a${...}on the node is never evaluated, and a key underpropsnever reaches aui:*/page:*renderer at all" (both facts are the rule's own opening paragraph, L105–L111, and rows 2 and 4 of its table). No re-wrap counted as payment..changeset/27243-objectui-action-variant-eval.md:'@objectstack/skills': patch, carrying both declaration lines.Every claim in the line, against
origin/main72b08eec:variantenum isdefault,danger,outline,secondary,ghost,link(+primaryonaction:button)packages/spec/src/ui/component.zod.tsL3451ACTION_BLOCK_VARIANTS; L3944actionBlockVariant("Anaction:button's", ['primary'])destructiveis refused on an action block, with the renamecomponent.zod.tsL3460–L3463actionBlockVarianterror map keyed onissue.input === 'destructive'; L3472–L3477refuseActionBlockDestructiveVariant; the protocol-18 entrypackages/spec/src/migrations/entries/semantic/18.ui-page-action-block-variant-destructive-refused.tssays where it is met: "the next save of the page meets the component-props gate at the metadata save door;os validatereports the old value as acomponent-props-invalidfinding"dangerdraws the destructive button and the confirm button destructivecomponent.zod.tsL3945 describe;ui/packages/components/src/renderers/action/runner-variant.tstoButtonVariant(danger→destructive) andtoRunnerVariant;ui/packages/components/src/renderers/action/action-button.tsxL207–L209 and L330–L333;ui/packages/core/src/actions/ActionRunner.tsL551–L553 and L1611–L1613 (confirmOptions: adangeraction paints the confirm button destructive)destructiveis the Button primitive's own wordui/packages/components/src/ui/button.tsxL21 variantdestructive;component.zod.tsL2230 and L2342 describe it as "the Button primitive's own vocabulary"packages/spec/src/conversions/registry.tsL11547page-action-block-variant-destructive-to-danger,toMajor: 18Tests
Token budget (
ceil(utf8 bytes / 4), the ratchet's convention; no ceiling moves):skills/objectui/rules/protocol.mdskills/objectui/evals/page-builder.jsonSKILL.mdunderskills/is unchanged (none touched); theskills/objectuibundle is 202254 bytes before and 202290 after (git ls-tree -r -l); the whole shipped tree reads 219136 → 219145 tokens on the ratchet's bundle-total line.node ui/scripts/check-skill-eval-tokens.mjsexits 0 before and after ("Every must_contain token is taught by its own skill bundle"). After this PR the bundle teaches"variant": "danger"andconfirmTextas whole tokens, which the pending eval case'smust_containneeds.Acceptance notes
skills/objectui/evals/page-builder.json(it owns the action-block cases, 1 and 3;protocol.json's diagnostic shape would put the refused spelling in the prompt and fail an answer that quotes it). The case: prompt "Add a 'Delete product' button to the product detail page. It must read as destructive and ask the user to confirm before the record is removed."; expected output names anaction:buttonwhosepropertiescarrylabel,actionType,target, aconfirmTextquestion andvariant 'danger', red and confirming through the destructive confirm button, with the primitive's spelling refused on a block at save;must_containaction:button,"variant": "danger",confirmText;must_not_contain"variant": "destructive"(a JSON key-value literal, so an answer that saysdestructiveis the primitive's word, or showsvariant="destructive"on the React Button, still passes; the expected output does not spell it). Cost: the file is 2238 bytes against a 2240-byte ceiling. With the case written lean (457 bytes of prose, 790 with its JSON frame) and every existing prompt and rubric tightened the way PR docs(skills): objectstack-data, -automation and -platform teach ownership queues and assignment rules (ADR-0142 phase 2) with one eval each #27426 paid (everymust_containtoken and fact kept, 1206 → 914 bytes of prose), the file reads 2735 bytes = 684 tokens: +124 over the ceiling, and nothing left to delete without losing an assertion. The route the ratchet header records for this shape is the 2026-10-27 ruling 「A:先削重复,余额按实测提高」(decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」), under whichCEILINGSrows moved +160, +194, +224, +286 and +297 on 10-27/10-28 for evals that could not be paid down. The claim for this card forbids a raise, so the case is held: the drafted fixture is in the report on the card, ready for a patch round on this PR once the seat rules.packages/spec/src/migrations/entries/semantic/18.ui-page-action-block-variant-destructive-refused.tsreasonsays "Other components that draw the Button primitive directly (element:buttonand the like) keepdestructive".ElementButtonPropsSchema.variant(component.zod.tsL2968) isprimary | secondary | danger | ghost | link— nodestructive— andui/packages/components/src/renderers/basic/elements.tsxL308–L314 mapsdanger→ shadcndestructive. The card body repeats the same sentence ("destructivestays the word for the Button primitive (element:button)"). The entry's text is compiled into the upgrade guide (gen:upgrade-guide), so the wrong sentence ships. This PR's line says "the Button primitive's word (variant="destructive"in React)" and does not nameelement:button. Not fixed here; noted for the seat (dedupe words:element:button destructive,upgrade guide variant,18.ui-page-action-block-variant-destructive-refused).marchtianwith the configurednoreply@anthropic.comaddress: a-c user.nameI passed on the commit by mistake (the worktree's configureduser.nameisClaude). An amend plus--force-with-leasewas refused by the auto-mode classifier (Git Destructive) and not retried; the trailer pair is model-free and correct.check:dts-closure,check:dual-build-cjs-loads,check:lean-entry-closure,check:sourcemap-no-sources-content(exit 3: nodist/for most packages),@objectstack/lint check:doc-formula-expressions(exit 3:@objectstack/formulanot built).Verification
Head
133ce6d8onorigin/main72b08eec(no merge needed: the branch is one commit on the current main). Derived withnode scripts/pm/dispatch-gates.mjs --commands(no paths; 45 commands from the committed diff).Derived gates: 40 exit 0, 5 exit 3 (prerequisite), 1 measured in two legs
check-skills-token-ratchet(84 authored bundle files within their ceilings) and its--self-test;@objectstack/spec check:skill-examplesunder the verify lock afterpnpm --filter '@objectstack/client-react...' build(VERDICT command-exit 0; "274 prose examples type-check across 3 surface(s)");check-adr-0087-registration --base origin/mainand--self-test;check-changeset-no-major;check-empty-changesetand--self-test;check-closing-keyword-parityand--self-test;check-comment-mask-corpus;check-doc-route-spelling --advisoryand--self-test;check-dts-emitted --self-test;ci/assert-turbo-remote-cache,ci/plan-test-core-shards,ci/prune-turbo-cache,pm/release-rehearsal-clone,release-pending-publish--self-test;check:agent-test-spelling,check:changeset-gate-self-tests,check:ci-filter-parity,check:closed-ceremony-text,check:corpus-claim-drift,check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:gitlink-declared,check:nul-bytes,check:pm-changeset-deadline-census,check:pm-governed-merges,check:pm-skill-id-lint,check:published-files,check:refd-timer-probe,check:role-word,check:skill-compatibility,check:skill-frame-sync,check:skill-identifier-liveness,check:watch-hint-literal.check:dts-closure,check:dual-build-cjs-loads,check:lean-entry-closure,check:sourcemap-no-sources-content,check:doc-formula-expressions.pnpm --dir ui exec vitest run --maxWorkers=2 scripts/under the verify lock, two legs. Leg 1, before any package was built: VERDICT command-exit 1, Test Files 16 failed / 153 passed / 2 skipped (171), Tests 56 failed / 4574 passed / 17 skipped; every failure isCannot find package '@objectstack/spec/...'(15 files) ordate-only-zone-pins-10183.test.ts, whose spawned plugin suites "ran no case at all" on the unbuilt ui workspace. Leg 2, after building@objectstack/specand the client-react closure: the 15 spec-dependent files plus the three skill gates' tests (check-skill-eval-tokens,check-skill-examples,check-skills-paths) re-run asvitest run check-skill check-doc-expression-carriage spec-symbol-ledger dollar-dialect check-component-surface-parity check-designer-field-key-parity check-action-forward-parity check-doc-json-fences check-doc-snippet-types console-optimize-deps: VERDICT command-exit 0, Test Files 19 passed (19), Tests 674 passed (674).date-only-zone-pins-10183.test.tsstays NOT MEASURED locally (it needs the ui plugin packages built); CI's Test UI runs it.dispatch-gates --ranwith each exit code recorded (the vitest row recorded as its leg-1 exit 1): 45 derived families accounted for — 40 run, 5 NOT-MEASURED, derived from the recorded exit 3; exit 0.Also run, outside the derived list:
node ui/scripts/check-skill-eval-tokens.mjsexit 0;grep -naPfor raw control bytes over the two touched files finds none.Generated by Claude Code