Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/27243-objectui-action-variant-eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@objectstack/skills': patch
---

docs(skills): the objectui action-block rule teaches `variant: 'danger'` for a destructive action and that `destructive` is the Button primitive's word, refused on an action block

Clause-②: no
Metadata-protocol: none

- **Before.** `rules/protocol.md` taught `action:button` without its `variant`. An agent that wanted a red delete button had only the Button primitive's `destructive` to copy from the React examples, and `variant: 'destructive'` on an action block is refused at save with the rename to `danger`.
- **Now.** The "Actions Are Node Types" rule carries one line: `"variant": "danger"` draws the action block red and asks its `confirmText` question through the destructive confirm button; `destructive` stays the Button primitive's word (`variant="destructive"` in React) and is refused on an action block. Paid in the same file: a provenance sentence about the retired `dataSource` measurement route and a sentence restating the `props` envelope rule, both stated elsewhere in the file, are gone.
- **Eval.** `evals/page-builder.json` gains a case that asks for a destructive delete action: it must spell `action:button`, `"variant": "danger"` and `confirmText`, and fails on `"variant": "destructive"`. The three existing cases are tightened with every assertion kept, and the file's ratchet ceiling moves 560 → 684 tokens, the measured remainder (ruling 2026-10-27, 「A:先削重复,余额按实测提高」).
- No schema, key or behaviour changes.
17 changes: 16 additions & 1 deletion scripts/check-skills-token-ratchet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,22 @@ export const CEILINGS = new Map([
['skills/objectui/evals/data-integration.json', 471],
['skills/objectui/evals/i18n.json', 439],
['skills/objectui/evals/mobile.json', 452],
['skills/objectui/evals/page-builder.json', 560],
// 560 -> 684 (card #27243, the `Skills-docs: follow-up owed` of the
// `destructive` -> `danger` rename on the action blocks): eval 4, 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`, and `"variant": "destructive"` (the Button primitive's
// word, refused on an action block at save) is a `must_not_contain`, spelled
// as the JSON key-value so a correct answer naming the 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, -292).
// Arithmetic in priced bytes: 2238 -> 2735 (+789 added, -292 cut, +497 net)
// -> ceil(2735 / 4) = 684; headroom was 2 bytes, 0 tokens, so the ceiling
// moves +124, pinned AT the measurement. Maintainer ruling 2026-10-27 on the
// seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」,
// tap A verbatim and untranslated: 「A:先削重复,余额按实测提高」.
['skills/objectui/evals/page-builder.json', 684],
['skills/objectui/evals/plugin-development.json', 492],
['skills/objectui/evals/project-setup.json', 786],
['skills/objectui/evals/protocol.json', 761],
Expand Down
28 changes: 22 additions & 6 deletions skills/objectui/evals/page-builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"evals": [
{
"id": 1,
"prompt": "We have a React + Vite internal app for inventory. Please create a schema-driven list page for products with search, status filter, row actions, and a create-product action. Use ObjectUI patterns and include integration snippets.",
"expected_output": "Produces a complete page schema JSON for list/grid behavior, includes integration wiring steps for SchemaRenderer and component registration, and provides a validation checklist.",
"prompt": "Create a schema-driven list page for products in our React + Vite inventory app: search, status filter, row actions and a create-product action, with ObjectUI integration snippets.",
"expected_output": "A complete list/grid page schema JSON, SchemaRenderer wiring and component registration steps, and a validation checklist.",
"files": [],
"assertions": {
"must_contain": [
Expand All @@ -22,8 +22,8 @@
},
{
"id": 2,
"prompt": "Please convert our existing customer detail page to Schema-Driven UI. It needs three sections — Basic Info, Contacts, and Recent Orders — and the 'Edit Customer' button must be visible only to admins. Stack: React + TypeScript.",
"expected_output": "Provides a structured detail-page schema with expression-based visibility for admin-only actions, keeps sections modular, and explains how to integrate into an app project.",
"prompt": "Convert our customer detail page (React + TypeScript) to schema-driven UI: sections Basic Info, Contacts and Recent Orders, and an 'Edit Customer' button visible only to admins.",
"expected_output": "A detail-page schema with modular sections, expression-based visibility on the admin-only action, and app integration steps.",
"files": [],
"assertions": {
"must_contain": [
Expand All @@ -39,8 +39,8 @@
},
{
"id": 3,
"prompt": "Build a console-like analytics dashboard page in an existing ObjectUI project: KPI cards on top, trend chart in the middle, and recent activity list at the bottom. Also include action definitions for refresh and navigate-to-report.",
"expected_output": "Generates dashboard schema with layout composition, plugin-oriented chart usage, action definitions as data, and testing guidance for responsiveness and interaction.",
"prompt": "Build an analytics dashboard page in an existing ObjectUI project: KPI cards on top, a trend chart, a recent-activity list at the bottom, plus refresh and navigate-to-report actions.",
"expected_output": "A dashboard schema with layout composition, a plugin chart, actions as data, and responsiveness and interaction testing guidance.",
"files": [],
"assertions": {
"must_contain": [
Expand All @@ -51,6 +51,22 @@
],
"must_not_contain": []
}
},
{
"id": 4,
"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": "An action:button whose properties carry label, actionType, target, a confirmText question and variant 'danger': the button draws red and asks that question through the destructive confirm button. On an action block the Button primitive's spelling of that arm is refused at save; danger is the action runner's word.",
"files": [],
"assertions": {
"must_contain": [
"action:button",
"\"variant\": \"danger\"",
"confirmText"
],
"must_not_contain": [
"\"variant\": \"destructive\""
]
}
}
]
}
11 changes: 7 additions & 4 deletions skills/objectui/rules/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ the same bag, and `SchemaRenderer` evaluates it and then **hoists every key onto
the node** (`type` / `id` excepted) before the renderer runs — so it is read by
every namespace, not just `element:*`. Measured on `origin/main` `f1c27f037`
with a host scope carrying `data = { label: "Evaluated Title" }` (published
through `PredicateScopeProvider`). The `dataSource` wiring this was first
measured through is retired; the envelope readings below are unchanged by that:
through `PredicateScopeProvider`):

| node | rendered card header |
|---|---|
Expand All @@ -159,8 +158,7 @@ one this skill teaches. The last row above is real and is the only spelling that
carries an expression into a key a `ui:*` / `page:*` renderer reads, but whether
`properties` is an official authoring channel for those namespaces is an open
contract question, so it is recorded here rather than
recommended. What is *not* open: a `${...}` on the node is never evaluated, and a
key under `props` never reaches a `ui:*` / `page:*` renderer at all.
recommended.

## Rule: No Schema Property Invention

Expand Down Expand Up @@ -274,6 +272,11 @@ component refuses a node-level `actionType` / `target` as mis-layered
}
```

**Destructive actions:** `"variant": "danger"` draws the button red and asks its
`confirmText` question through the destructive confirm button. `destructive` is
the Button primitive's word (`variant="destructive"` in React); on an action
block the spec refuses it at save, naming the rename.

**❌ DO NOT** use function references or inline callbacks in JSON schemas, and
**❌ DO NOT** author an `events` bag. `BaseSchema` declares no `events` member and no
renderer reads `schema.events`: a typed literal refuses it, and one
Expand Down
Loading