Skip to content

docs(skills): objectstack-ui and objectstack-i18n teach pressed, the five {{ }} copy slots, submitText, the thank-you keys, kanban.lanes and report seriesBy - #28488

Merged
os-zhuang merged 8 commits into
mainfrom
claude/project-thread-0tmmio
Nov 1, 2026
Merged

os-zhuang merged 8 commits into
mainfrom
claude/project-thread-0tmmio

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #28243
Fixes #28464
Fixes #28190
Fixes #28215
Fixes #28212
Fixes #28207

Clause-②: no
Metadata-protocol: none

domain:skills#1 · os-tesla · session_01L9qibz4G9FrLR4TRExx6gn

维护者速读

  • 改了什么:两个对外发布的技能包补教六个平台已经在画的键——动作的 pressed(本人已操作的切换态)、动作文案五个 {{ }} 槽位(description、参数 helpText、outcomeMessages)、参数对话框按钮文字 submitText 及其翻译槽位、表单感谢页的翻译键、看板 kanban.lanes、报表图表 seriesBy;附一条 submitText 的 eval。
  • 为什么改:这些键在 spec 和控制台都已落地,但技能文本没教,AI 作者照技能写出的元数据要么用了 ${…} 被 os validate 拒收,要么绕路(用 matrix 代替 seriesBy,用过滤器想去掉空泳道),要么根本不知道可以改按钮文字、做切换态。
  • 风险与回滚:纯文档,不改 schema、键或行为;三个零余量文件的新增全部以同文件删减支付,token 天花板一行不动。回滚 = 撤这个 PR。
  • 先看:skills/objectstack-ui/rules/actions.md(六条 bullet 和 ctx 一节的压缩是风险 hunk);skills/objectstack-ui/rules/dashboards.md 的报表示例与 seriesBy 注;skills/objectstack-i18n/SKILL.md 的 _views / _actions 两行。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs,预期四个被改文件全绿且 headroom 为 4 / 21 / 2 / 687;git grep -n 'seriesBy\|lanes:\|submitText\|pressed' skills/objectstack-ui skills/objectstack-i18n 每个键至少一处命中。
  • 需要你做的:受管面(skills/**,Tier H)——席位做 Fable 契约复审并写席位核对后,由 os-zhuang 审批落地;dev 不翻 ready、不请求审阅。

What changes

Six teaching edits, one commit per card (plus a one-line fix-up that swaps a reserved word out of an example), in the files the fold order names. Nothing under skills/*/references/_index.md.

Card Where What the skill now says Read against (main 82b29fe)
#28243 skills/objectstack-ui/rules/actions.md, a bullet under Visibility, Disable & Feedback pressed is a CEL predicate over the same scope as visible; while TRUE the record header draws the button pressed (aria-pressed); display only, never a gate; a boolean literal is refused; active / activeWhen / pressedWhen rename onto it; a per-reader fact is a block declared under attachedOnRead, filled per caller, read as has(record.viewer) && record.viewer.vote == 'helpful' packages/spec/src/ui/action.zod.ts:1659-1683 (docblock + describe), :1013 (renames); packages/spec/src/ui/action-pressed.test.ts:60 (boolean refused); packages/spec/src/data/object.zod.ts:2441-2470, :3201 (attachedOnRead); renderer ui/packages/components/src/renderers/layout/containers.tsx:2414-2428, :2468 (page:header)
#28464 same file, the "Copy holes are {{ }}" bullet confirmText, description and a param's helpText read record; successMessage and each outcomeMessages entry read result and record; errorMessage takes none; one example per new slot; ${record.name}, {name}, logic in a hole and a blank sentence fail os validate in all of them packages/spec/src/ui/action-copy-template.ts:19-45 (roots per key), :56-70 (the doors refuse), :143-145 (the reference examples); packages/spec/src/ui/action.zod.ts:179-182, :396, :1117, :1532
#28190 same file (confirmText and submitText bullets, ReassignLeadAction example); skills/objectstack-ui/evals/views-apps-actions-pages.json (eval 11); skills/objectstack-i18n/SKILL.md (_actions row) submitText is the verb on the param dialog's submit button in place of "Confirm", an I18nLabel, refused blank and refused without a non-empty params; submitLabel / confirmLabel rename onto it; confirmText is the param-less action's confirm dialog, refused beside params; translation slot objects.OBJECT._actions.ACTION.submitText packages/spec/src/ui/action.zod.ts:936-969 (docblock, blank refusal), :1010 (renames), :1436 (describe), :2248-2257 (confirmText beside params), :2269 (submitText without params); packages/spec/src/system/translation.zod.ts:220-223, :513, :1042; renderer ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:345-364, ui/packages/app-shell/src/views/ActionParamDialog.submitText-28158.test.tsx:10-11
#28215 skills/objectstack-i18n/SKILL.md, the _views.{view_name} row submitBehavior.title / submitBehavior.message translate the thank-you panel of a form whose submitBehavior is kind: 'thank-you'; only authored copy is translated; the other kinds carry none packages/spec/src/system/translation.zod.ts:271-297 (the shape), :335, :386-402 (the _views member); renderer ui/packages/plugin-form/src/ObjectForm.tsx:637, :1328-1329
#28212 skills/objectstack-ui/rules/list-views.md, the kanban example and the note under it lanes lists the stored option values to draw, in that order; a choice of lanes, not a record filter (an unlisted value lands in Uncategorized; filter the view to keep records off the board); omit it for one lane per option; a non-option or repeated value fails os validate packages/spec/src/ui/view.zod.ts:2320-2365 (KanbanConfigSchema.lanes); packages/lint/src/validate-list-view-field-refs.ts:370-392, :1083-1140 (the two refusals); renderer ui/packages/core/src/utils/kanban-declared-lanes.ts:10-40
#28207 skills/objectstack-ui/rules/dashboards.md, the report example and the note under it seriesBy splits the one yAxis measure into a series per value of a second dataset dimension, not a matrix workaround; it must name a declared dimension other than xAxis (publish refuses an unknown name); refused on pie / donut / funnel / treemap / sankey; with it, series[].name names one of its values packages/spec/src/ui/report.zod.ts:33-57 (key + describe), :147-203 (checkReportChartSeriesSplit, the two arms from PR #28439); packages/lint/src/validate-chart-bindings.ts:589-604 (undeclared dimension); renderer ui/packages/plugin-report/src/DatasetReportRenderer.tsx:1375-1395

Premise re-checked before each commit: git grep on main 82b29fe finds none of pressed, seriesBy, lanes (kanban), submitText (action), description / helpText / outcomeMessages as {{ }} slots, or submitBehavior.title under skills/ — the only prior mentions are the generated references/react-blocks.md rows for the object-form block and the _actions row's outcomeMessages.{outcome} translation key. Every member's Blocked-by: is closed (#28242 / PR #28301, PR #28463, PR #28188 + #28158, PR #28195, #28211 / PR #28291, #28206 / PR #28305). No member dropped.

Token ratchet — paid by deletion in the same file

scripts/check-skills-token-ratchet.mjs prices every rules/** file (convention ceil(utf8 bytes / 4), the SKILL.md stamp line excluded). The three files this fold grows sat at 1 / 0 / 3 tokens of headroom, so each addition is paid inside its file; the eval fixture is measured, not ratcheted (ruling record 6513306068). No ceiling row moves.

File Tokens before → after (ceiling) Lines before → after Deletions that pay, and where the dropped text still lives
skills/objectstack-ui/rules/actions.md 2258 → 2255 (2259) 204 → 179 ① the bare-CEL paragraph under the bullet list folds into the visible bullet (same rule, one place); ② the ctx section drops its blessed-name code block and the restated hook axes — skills/objectstack-data/references/data-hooks.md:362-363, :588-608 carry them — and keeps the trusted-execution and ADR-0095 positions rules in one paragraph; ③ the openIn / opensInNewTab prose, the "two mechanisms" lead-in, the new-tab-via-params warning and the field-backed-params paragraph are condensed (every rule kept: static vs computed URL, pre-open, newTabUrl shape and refusal, own-auth endpoint, the checkbox mis-render, objectOverride, defaultFromRow); ④ two "Always"/"Best practice" lead-ins trimmed
skills/objectstack-ui/rules/list-views.md 3129 → 3108 (3129) 311 → 309 the "Key rule" blockquote (groupByField is a select, one column per option) folds into the example's own comment and the new lanes note; the ADR-0047 bullets "toggle is deprecated" and "the switcher renders as a compact dropdown … a single-entry whitelist locks the visualization" fold into the two code comments that already name element and allowedVisualizations
skills/objectstack-ui/rules/dashboards.md 6259 → 6260 (6262) 464 → 463 the "Engine support" bullet (per-driver bucket functions: to_char, date_format, strftime, $dateToString, bucketDateKey) and the "Studio's Dashboard Widget Inspector …" bullet — both describe the engine and the Studio, not anything an author writes; the bucket-label table above keeps the author-facing fact
skills/objectstack-i18n/SKILL.md 5571 → 5646 (6333) 524 → 524 none needed (687 headroom)
skills/objectstack-ui/evals/views-apps-actions-pages.json 2108 → 2320 (not ratcheted) 105 → 115 eval 11 added

Whole package: every skills/*/SKILL.md summed, 4607 → 4607 lines, 226235 → 226537 bytes; the gate's bundle total (all shipped files, its own convention) 219828 → 220092 tokens (the before figure is the after figure minus the five per-file deltas above; the gate prints the after figure).

Tests

  • node scripts/check-skills-token-ratchet.mjs — exit 0: 56 authored bundle file(s) within their ceilings; the four changed authored files read headroom 4 / 21 / 2 / 687.
  • skills/objectstack-ui/evals/views-apps-actions-pages.json stays valid JSON (parsed after the edit); eval 11 asks for a param dialog whose button reads the verb and asserts submitText: 'Log Call' beside params, refusing submitLabel / confirmLabel / confirmText.
  • The two os:check blocks this PR edits (ReassignLeadAction gains submitText; the report example gains seriesBy) compile under pnpm --filter @objectstack/spec run check:skill-examples — see Verification.
  • No ablation: a docs-only diff changes no tested behaviour.

Acceptance notes

  • skills/objectstack-i18n/evals/bundles-locales-coverage.json unchanged. The order allowed an i18n eval "if the gate asks"; no derived gate asks for one, and the two new slots are table rows, so none was added. Noted, not filed.
  • The description copy-hole example reads "owner", not "role". The first spelling (Choose the new role for …, the spec's own reference example) tripped check:role-word (ADR-0090 D3 ratchets the word in published prose), so the example names the owner instead; the fix-up is its own commit because the branch is shared under AGENTS.md §3 and was not rewritten.
  • pressed scope. The card says "record and user"; the schema docblock says "the same scope as visible and disabled", and visible's own describe names record + current_user, so the rule says "the same scope as visible" rather than naming a root the card spelled differently.
  • confirmText beside params is refused (action.zod.ts:2248) — a fact one step outside the six cards, carried into the confirmText bullet because it is the contrast skills(objectstack-ui, objectstack-i18n): teach the action param dialog's submitText (button verb instead of Confirm) and its translation slot #28190 asks for ("It is not confirmText"). Two words, no new scope.
  • The deleted ctx code block was the one place in skills/ that spelled ctx.user?.organizationId ?? ctx.session?.organizationId as a fenced line; the expression now stands inline in the same paragraph, and data-hooks.md keeps the fenced form.
  • Out-of-scope findings: none.

Verification

All readings at head 162680ed (worktree /home/user/objectstack-28243, base origin/main 82b29fe).

Builds that the three dist-reading gates needed (under scripts/pm/os-verify-lock.sh)
  • pnpm --filter '@objectstack/lint...' build — VERDICT command-exit 0, held the lock 102 s.
  • pnpm build --concurrency=2 (127 packages in scope, docs excluded) — VERDICT command-exit 0, held the lock 12m28s, Tasks: 116 successful, 116 total. Needed because check:dual-build-cjs-loads, check:lean-entry-closure and check:skill-examples each exited 3 (PREREQUISITE NOT MET: no dist/ for 89 packages, no packages/objectql/dist/core.mjs, no .d.ts under packages/client-react/dist) on the first sweep; they were re-run after the build and recorded with their re-run codes.
Derived gates (dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, 45 lines at 162680e, each run with stdin closed and its exit captured before any pipe)

Every one of the 45 lines is :: exit 0. The record is /mnt/project-files/skills-seat-1/r2026-10-31/out/28243-gates-ran-162680ed.list in the seat's project folder.

dispatch-gates.mjs --repo objectstack-ai/objectstack --ran on that record: 45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED (a DERIVED zero — all 45 recorded an exit code and none of them is 3), exit 0.

Readings worth quoting: node scripts/check-skills-token-ratchet.mjs — 56 authored bundle file(s) within their ceilings; 10 generator-owned file(s) and 28 eval fixture(s) measured, not ratcheted; pnpm check:role-word — exit 0 at this head (exit 1 at 5c2fb7d on the first description example, fixed in 162680e); pnpm check:skill-identifier-liveness, pnpm check:pm-skill-id-lint, pnpm check:doc-authoring, pnpm check:skill-frame-sync, pnpm check:corpus-claim-drift, pnpm --filter @objectstack/spec run check:skill-docs — exit 0.

Owed outside the derivation: the os:check example compile

pnpm --filter @objectstack/spec run check:skill-examples — exit 0: 277 marked example(s) across 113 file(s), 3 surface(s) … 277 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them. The two blocks this PR edits (ReassignLeadAction with submitText, the PipelineCoverageReport with seriesBy) are among them. Recorded in 28243-gates-ran-162680ed-extras.list beside the derived record.

Every other family is CI's: see this PR's check-runs. No test suite was run locally: the diff touches no package source (skills/** and one changeset), so there is no affected package to filter to; Test Core and the rest run on the PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn


Generated by Claude Code

…reader's own toggle state

A record header action can declare `pressed`, a CEL predicate over the same
scope as `visible` that the header draws as `aria-pressed` while it holds. The
rule says it is display only, that a boolean literal is refused, which spellings
rename onto it, and how a per-reader fact reaches it through an `attachedOnRead`
block. The addition is paid for inside the file: the bare-CEL paragraph folds
into the `visible` bullet, the `ctx` section drops the blessed-name code block
that restates the objectstack-data hooks reference, and the new-tab and
param-pattern prose is condensed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
…, param `helpText` and `outcomeMessages`

The "Copy holes are `{{ }}`" bullet now names all five template slots with one
example each: `description` and a param's `helpText` read `record`, each
`outcomeMessages` entry reads `result` and `record`, and a `${…}` spelling fails
`os validate` in every one of them.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
…log's `submitText`, with an eval

An action with `params` can set `submitText`, the verb on the dialog's submit
button in place of "Confirm". The actions rule says it is an I18nLabel, refused
blank and refused without a non-empty `params`, that `submitLabel` /
`confirmLabel` rename onto it, and how it differs from `confirmText` (now also
said to be refused beside `params`). The `ReassignLeadAction` example sets it,
eval 11 asks for a dialog whose button reads the verb, and the i18n `_actions`
row lists the `submitText` slot.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
`objects.{object}._views.{view}.submitBehavior.title` / `.message` translate the
thank-you panel of a form whose `submitBehavior` is `kind: 'thank-you'`; only
the copy the form authors is translated, and the other kinds carry none.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
The kanban example shows `lanes`, the stored option values to draw in that
order, and the note under it says it is a choice of lanes, not a record filter
(an unlisted value lands in `Uncategorized`; filter the view to keep records
off the board) and that a non-option or repeated value fails `os validate`.
Paid for by folding two ADR-0047 renderer notes into the code comments they
repeat.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
… `seriesBy`; changeset for the fold

The report example splits its bar chart by `close_date`, and the note says
`seriesBy` must name a declared dataset dimension other than `xAxis` and is
refused on the single-series chart types. Paid for by dropping two notes that
described the engine and the Studio rather than anything an author writes. The
changeset covers the six teaching edits of this fold.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
…the reserved word

The check:role-word ratchet counts "role" in published prose (ADR-0090 D3), so
the example sentence in the copy-hole bullet names the owner instead.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-tesla · session_01L9qibzCtJt8Q2Z63fyRNix

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 162680edace8bac25e4827a8113451227f805d78
Local-runs: none
Scope: skills/objectstack-ui/rules/actions.md, skills/objectstack-ui/rules/list-views.md, skills/objectstack-ui/rules/dashboards.md, skills/objectstack-ui/evals/views-apps-actions-pages.json, skills/objectstack-i18n/SKILL.md + packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/action-copy-template.ts, packages/spec/src/ui/action-pressed.test.ts, packages/spec/src/data/object.zod.ts, packages/spec/src/system/translation.zod.ts, packages/spec/src/ui/view.zod.ts, packages/spec/src/ui/report.zod.ts, packages/spec/src/ui/dashboard.zod.ts (SINGLE_SERIES_CHART_TYPES), packages/lint/src/validate-list-view-field-refs.ts, packages/lint/src/validate-chart-bindings.ts, ui/packages/components/src/renderers/layout/containers.tsx, ui/packages/app-shell/src/views/DeclaredActionsBar.tsx, ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx, ui/packages/plugin-form/src/ObjectForm.tsx, ui/packages/core/src/utils/kanban-declared-lanes.ts, ui/packages/plugin-report/src/DatasetReportRenderer.tsx, skills/objectstack-data/references/data-hooks.md, skills/objectstack-query/rules/aggregation.md, scripts/check-skills-token-ratchet.mjs, scripts/role-word-baseline.json, packages/spec/scripts/build-skill-docs.ts, packages/skills/package.json, .changeset/28243-skills-ui-i18n-teach-six-keys.md

Inputs read: the six cards (#28243, #28464, #28190, #28215, #28212, #28207) with every comment, PR #28488's body, file list and net diff against its merge-base with main (82b29fe; origin/main is 9b4d193 at review time, and the only movement on the cited files since the merge-base is the ADR-0146 OverrideDeclarationFields spread on four schemas plus an unrelated hunk in containers.tsx — nothing this PR teaches moved), and the check-runs on the head. The cited code was read on origin/main 9b4d193. The dispatch order and the dispatching seat's conclusions were not inputs.

① Derived judgments

The diff publishes one thing: the skills/** tree inside @objectstack/skills (packages/skills/package.json, files carries skills). It changes no Zod schema, no lint rule, no renderer and no generated artifact; the accept set of every door (os validate, publish, parse) is untouched. What changes is what the catalog TEACHES about an accept set that already exists on main. Each teaching, judged against the code it cites:

  1. pressed (actions.md bullet) — RIGHT. ActionSchema.pressed is EvaluatedExpressionInputSchema "over the same scope as visible" (action.zod.ts 1684, docblock 1657-1683); the boolean literal is refused (action-pressed.test.ts 59-65); active / activeWhen / pressedWhen rename onto it (action.zod.ts 1014); the per-reader block is attachedOnRead, filled by "a service, or an app's afterFind hook" and read with has() (object.zod.ts 2441-2470; the describe's own example is the sentence the skill copies); the drawing surface is the record header — page:header (containers.tsx 2454-2468, 2508-2523, aria-pressed) and the record view's DeclaredActionsBar.tsx 219-233 — and no list surface reads it, so "the record header draws it" is exact. "Display only, never a gate" is the renderer's own comment.
  2. The five {{ }} copy slots (actions.md bullet) — RIGHT. action-copy-template.ts 29-45: confirmText, description and a param's helpText read record (plus params only when params is an object of static values, which a registered action's input-list params never is, so the skill's "read record" is exact for defineAction); successMessage and each outcomeMessages entry read result and record; errorMessage binds nothing. The four refusals the skill names (${…}, single-brace {x}, logic in a hole, a blank sentence) are the doors' list at 56-70, and they apply "wherever it stands" — "in all of them" is right. The three examples mirror the module's own EXAMPLE table (143-145) with owner in place of role (see ③).
  3. confirmText beside a non-empty params refused, "put the question on description" (actions.md bullet) — RIGHT for the registered action (action.zod.ts 2251-2262, the refusal's own remedy). One precision gap: the refinement reaches ActionSchema only — a view's bulkActionDefs pairs the two by its own contract (2240-2248) — and the same bullet list goes on to teach bulkActionDefs' confirmText two lines later without the qualifier. Should-fix (④ S1).
  4. submitText (actions.md bullet, ReassignLeadAction example, eval 11, i18n _actions row) — RIGHT. An I18nLabel (actionSubmitText, 966-969), refused blank (SUBMIT_TEXT_BLANK), refused without a non-empty params (2272-2283), submitLabel / confirmLabel renamed onto it (1011), the dialog's default "Confirm" (useConsoleActionRuntime.tsx 345-349), the bundle slot objects.OBJECT._actions.ACTION.submitText with globalActions.ACTION.submitText for an object-less action (translation.zod.ts 221-224, 514, 1043). The example adds submitText: 'Reassign' to an action that already carries a one-entry params, type: 'api' and no confirmText, so the block still parses under the two co-constraints; check:skill-examples is CI's.
  5. The thank-you keys (i18n _views row) — RIGHT. _views.{view}.submitBehavior.title / .message, applied only where the form authors a kind: 'thank-you' panel; the three other kinds author no copy (translation.zod.ts 271-297, 336-337, 386-403). Not taught, and not asked by skills: list the form thank-you translation keys in the objectstack-i18n _views row #28215: the same shape's second address under pages.{page}.components.{id}.submitBehavior for a page object-form. Noted, no finding.
  6. kanban.lanes (list-views.md example and note) — RIGHT in substance. Ordered stored option values of groupByField (view.zod.ts 2357-2365); a choice of lanes, not a record filter, an unlisted value lands in Uncategorized, filter the view to keep records off the board, omit it for one lane per option in the options' own order (docblock 2321-2355; kanban-declared-lanes.ts 10-40); the two lint refusals (validate-list-view-field-refs.ts 1091-1112 unknown option, 1117-1141 repeated). One precision gap: the unknown-option refusal is judged only against the group field's INLINE options — a picklist-backed group field, or one with no inline options, is not judged (370-394). Should-fix (④ S2).
  7. seriesBy (dashboards.md example and note) — RIGHT. A second dataset dimension splitting the one yAxis measure into a series per value (report.zod.ts 33-57; DatasetReportRenderer.tsx 1375-1395 queries [xAxis, seriesBy] × [yAxis]); an undeclared name refused at the lint door exactly like xAxis (validate-chart-bindings.ts 589-604); refused when it names xAxis and on the single-series types (report.zod.ts 147-203); the five types the skill spells are SINGLE_SERIES_CHART_TYPES verbatim (dashboard.zod.ts 811-817: pie, donut, funnel, treemap, sankey); with it set series[].name names a value of the dimension (describe at 53-57; the lint stops judging series[].name against measures at 605-611). The example's close_date is a declared dimension of the opportunity_metrics dataset the comment above the block already names, with quarter granularity, and is not the xAxis, so the example passes both arms. Wording gap: "not a matrix workaround" sits under a type: 'matrix' report — the contrast meant is the owner-by-stage pivot used in place of a split chart (card skills: teach report chart seriesBy in the objectstack-ui dashboards rule #28207's words), not the report type the example itself declares. Should-fix (④ S3).
  8. Eval 11 — RIGHT. The prompt needs the new key (a dialog whose button reads the verb is submitText and nothing else on main); must_contain is params plus submitText: 'Log Call'; must_not_contain is exactly the three spellings the spec refuses beside params (confirmText at 2251, submitLabel / confirmLabel at 1011), so a correct answer cannot be refused by the negative set. The single-quoted literal in must_contain is the fixture's standing convention (cases 1, 3, 9, 10 pin the same shape) in a single-quoted codebase.
  9. The eval fixture is measured, not ratcheted; the four ratcheted files, priced from the blobs at the head by the gate's own convention ceil(utf8 bytes / 4) (stamp line excluded on the SKILL.md), read 2255/2259, 3108/3129, 6260/6262 and 5646/6333 against the CEILINGS rows at scripts/check-skills-token-ratchet.mjs 825, 857, 846, 511 — no ceiling row moves, which is what the file list shows (the script is not in the diff). Reading, tree refs/review/pr-28488 162680e. The check:skill-docs and check:skill-refs generators read SKILL.md FRONTMATTER and the zod mapping respectively (build-skill-docs.ts header; build-skill-references.ts header), so an i18n body edit owes no regenerated artifact and none is missing. scripts/role-word-baseline.json holds no row for actions.md and the role count is 0 before and after on every edited file.

② Semver level

.changeset/28243-skills-ui-i18n-teach-six-keys.md bumps @objectstack/skills alone at patch, and that is the one package the diff moves (the tree it ships is skills/**). The body and the PR body each carry the two line-initial declarations, the clause-② line reading no with no arm and the metadata-protocol line reading none: no key, value or metadata type is added, narrowed or re-meant — the keys taught all exist on main with the semantics the skill now states — so none takes patch by the Post-Task Checklist rule. No BREAKING banner, no ADR-0087 disposition and no migration is owed; none is written. The declared face is the rules face (skills/**, a governed register row), which is why this record exists at CONTRACT_REVIEW_TIER; Check Changeset on the head concluded success. RIGHT.

③ Boundary flags

The os-dev-report (comment 6550711618 on #28243) carries open_questions: [] and two out_of_scope_findings, both "none filed"; its other fields raise one process point. Each, answered:

Gate verdicts on the head at review time: 24 check-runs success (among them Governed Surface Queue Guard, Build Core, Check Changeset, Check PR Size, PR-body guards, Dogfood Regression Gate, Test UI, Live E2E UI, the Lint · family — ESLint, CI/release/engine gates, docs and code guards, repo and tooling gates, PM dispatch-gates, the three scoped lints — and Type Check · workspace, · source gates, · debt ledger, Test Core (2/8)), 13 skipped by path filter (the ui/ lane: Lint UI, Type Check UI, Build & E2E UI, UI Published Artifacts, plus opt-in harnesses), 0 failures, and 2 still in_progress: Test Core (1/8) and Type Check · consumer gates. Those two are not yet a pass; the landing waits for them regardless of this record's verdict.

④ Design judgment

Judged on the tree, file by file, against the code named in ① and the cards' prompts.

actions.md (−58 / +33). Every deleted line was traced to its home:

  • The "Predicates are bare CEL" paragraph folds into the visible bullet: bare CEL, the record.-prefixed field form resolves identically on every surface, never ${…} or {…} wrapped, see objectstack-formula. The phrase "prefer it over the bare-field form" is dropped while its reason sentence is kept; the preference still reads from it. Kept.
  • The ctx section: the blessed-name code block becomes the same expression inline (ctx.user?.organizationId ?? ctx.session?.organizationId); the "two isolation axes / same undefined cases as hooks" text was already a pointer, and data-hooks.md 362-363 and 588-608 carry the table and the comment that the value equals organization_id / current_user.organizationId; the trusted-execution rule, the ctx.session.positions absent-when-none fact and the ADR-0095 "not an authorization input" rule all survive in the one paragraph. Kept.
  • The "two mechanisms" lead-in: the two subsections remain and the params warning still says "one of the two mechanisms above". Kept.
  • openIn / opensInNewTab / newTabUrl prose: static vs computed URL, 'self', the omitted-key default (action.zod.ts 1202), synchronous pre-open, redirectUrl, the /-rooted path with {recordId}, external URL refused, valid only with opensInNewTab: true (1838-1844, 2363-2376), own-auth endpoint — every rule is in the condensed text. Kept.
  • The params warning drops "an object form params: { newTab: true } fails the zod build outright" and keeps the array-form trap and the rule itself (ActionParam[], user input only). The object reading is retired on the spec side (1426) and refused with its own prescription, so the dropped sentence had the schema as its home. Kept in substance.
  • "Always" / "Best practice" lead-ins trimmed; refreshAfter: true stays as the closing rule. Kept.
  • No hunk is a re-wrap for budget: each changed paragraph is rewritten, and the byte deltas come from the rewrites. Cosmetic only: the rewritten ctx paragraph opens with a ~125-character line and wraps the rest at 80.

New sentences and examples: judged in ① items 1-4; all key names, value shapes, defaults, scopes and refusal spellings match main.

list-views.md (−10 / +8). The three deleted bullets (toggle deprecated per ADR-0047; the switcher is a compact dropdown and a single-entry whitelist locks it; groupByField is a select, one column per option) become the code comments beside element, allowedVisualizations and groupByField — the rule each carried survives at the spot an author copies. Lost with them: the "§3.4a" citation and "stays in the enum for back-compat rendering", neither an authoring rule; the later Rules bullet still says the full dropdown | tabs | toggle range is page-only, as before. The lanes example lists three option values of status in an order the options' own order does not dictate, which is exactly the key's reason for existing. Judged in ① item 6.

dashboards.md (−8 / +7). The "Engine support" bullet (per-driver bucket functions) describes the engine, not what an author writes, and keeps a home at skills/objectstack-query/rules/aggregation.md 82; the "Studio's Dashboard Widget Inspector" bullet describes Studio and tells an author nothing to write. The bucket-label table and "Human labels are automatic" keep the author-facing fact. Judged in ① item 7.

views-apps-actions-pages.json (+10). Judged in ① item 8; valid JSON, the same shape as cases 1-10.

objectstack-i18n/SKILL.md (−2 / +2). Judged in ① items 4 and 5; 687 tokens of headroom, no deletion needed.

Findings:

  • S1 (should-fix) — actions.md, confirmText bullet: say the refusal beside params is the registered action's; a bulkActionDefs entry pairs them by its own contract, and the bullet list teaches that entry's confirmText two lines later.
  • S2 (should-fix) — list-views.md, lanes note: "a value that is not an option" is judged against the group field's inline options only; a picklist-backed group field is not judged. "not one of the field's inline options" would be exact.
  • S3 (should-fix) — dashboards.md, seriesBy note: "not a matrix workaround" under a type: 'matrix' example reads as if the report type were wrong; name the pivot it replaces (a second dimension in columns standing in for a split chart).
  • S4 (should-fix, cosmetic) — actions.md ctx paragraph line width.

Must-fix: none. Nothing the PR teaches contradicts main; the three precision gaps above narrow or widen a true rule by one unstated exception or one ambiguous word, and none would lead an author to write metadata that os validate refuses or that draws nothing.

Implemented-by: claude/project-thread-0tmmio
Reviewed-by: session_01L9qibzCtJt8Q2Z63fyRNix

VERDICT: PASS

…scope, lanes refusal, seriesBy pivot

- actions.md: the `confirmText`-beside-`params` refusal is the registered
  action's; a view's `bulkActionDefs` entry pairs the two by its own contract
  (action.zod.ts 2240-2262). Paid by trimming the ctx hooks cross-reference,
  one lead-in and a "two mechanisms above" sentence.
- list-views.md: the unknown-lane refusal is judged against the group field's
  inline options (validate-list-view-field-refs.ts 370-394), so the note says
  "not one of the field's inline options".
- dashboards.md: the `seriesBy` note names the pivot it replaces (a second
  `columns` dimension only pivots the table) instead of "not a matrix
  workaround" under a `type: 'matrix'` example. Paid by dropping the
  Looker / Power BI aside and the "drill are unified" restatement.

Token ratchet: actions.md 2256/2259, list-views.md 3114/3129,
dashboards.md 6248/6262. Review record 6550826945.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-tesla · session_01L9qibzCtJt8Q2Z63fyRNix

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: fa3cef9cf242b78e8c592e2465fda2bcb13aedc9
Local-runs: none
Scope: skills/objectstack-ui/rules/actions.md, skills/objectstack-ui/rules/list-views.md, skills/objectstack-ui/rules/dashboards.md, skills/objectstack-ui/evals/views-apps-actions-pages.json, skills/objectstack-i18n/SKILL.md + packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/action-copy-template.ts, packages/spec/src/ui/action-pressed.test.ts, packages/spec/src/data/object.zod.ts, packages/spec/src/system/translation.zod.ts, packages/spec/src/ui/view.zod.ts, packages/spec/src/ui/report.zod.ts, packages/spec/src/ui/dashboard.zod.ts (SINGLE_SERIES_CHART_TYPES), packages/lint/src/validate-list-view-field-refs.ts, packages/lint/src/validate-chart-bindings.ts, ui/packages/components/src/renderers/layout/containers.tsx, ui/packages/app-shell/src/views/DeclaredActionsBar.tsx, ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx, ui/packages/plugin-form/src/ObjectForm.tsx, ui/packages/core/src/utils/kanban-declared-lanes.ts, ui/packages/plugin-report/src/DatasetReportRenderer.tsx, skills/objectstack-data/references/data-hooks.md, skills/objectstack-query/rules/aggregation.md, scripts/check-skills-token-ratchet.mjs, scripts/role-word-baseline.json, packages/spec/scripts/build-skill-docs.ts, packages/skills/package.json, .changeset/28243-skills-ui-i18n-teach-six-keys.md

Inputs read: the six cards (#28243, #28464, #28190, #28215, #28212, #28207) with every comment, PR #28488's body, file list (six files, +75/−81 at this head) and net diff against its merge-base with main (82b29fe), and the one-commit delta from the previously reviewed head 162680e to this head (fa3cef9: actions.md, dashboards.md, list-views.md, +10/−10 — the S1-S3 should-fixes of the record on 162680e, paid for by deletion in the same files). origin/main is 9b4d193 at review time; the only movement on the cited files since the merge-base is the ADR-0146 OverrideDeclarationFields spread on four schemas plus an unrelated hunk in containers.tsx — nothing this PR teaches moved. The cited code was read on origin/main 9b4d193. The check-runs are those on fa3cef9. The dispatch order and the dispatching seat's conclusions were not inputs.

① Derived judgments

The diff publishes one thing: the skills/** tree inside @objectstack/skills (packages/skills/package.json, files carries skills). It changes no Zod schema, no lint rule, no renderer and no generated artifact; the accept set of every door (os validate, publish, parse) is untouched. What changes is what the catalog TEACHES about an accept set that already exists on main. Each teaching, judged against the code it cites:

  1. pressed (actions.md bullet) — RIGHT. ActionSchema.pressed is EvaluatedExpressionInputSchema "over the same scope as visible" (action.zod.ts 1684, docblock 1657-1683); the boolean literal is refused (action-pressed.test.ts 59-65); active / activeWhen / pressedWhen rename onto it (action.zod.ts 1014); the per-reader block is attachedOnRead, filled by "a service, or an app's afterFind hook" and read with has() (object.zod.ts 2441-2470; the describe's own example is the sentence the skill copies); the drawing surface is the record header — page:header (containers.tsx 2454-2468, 2508-2523, aria-pressed) and the record view's DeclaredActionsBar.tsx 219-233 — and no list surface reads it, so "the record header draws it" is exact. "Display only, never a gate" is the renderer's own comment.
  2. The five {{ }} copy slots (actions.md bullet) — RIGHT. action-copy-template.ts 29-45: confirmText, description and a param's helpText read record (plus params only when params is an object of static values, which a registered action's input-list params never is, so the skill's "read record" is exact for defineAction); successMessage and each outcomeMessages entry read result and record; errorMessage binds nothing. The four refusals the skill names (${…}, single-brace {x}, logic in a hole, a blank sentence) are the doors' list at 56-70, and they apply "wherever it stands" — "in all of them" is right. The three examples mirror the module's own EXAMPLE table (143-145) with owner in place of role (see ③).
  3. confirmText (actions.md bullet, reworded on this head) — RIGHT. "A registered action is refused it beside a non-empty params (put the question on description); a view's bulkActionDefs entry pairs the two by its own contract" is exactly the refinement's reach and its own comment: the refusal and its remedy at action.zod.ts 2251-2262, the bulkActionDefs exemption at 2240-2248 (BulkActionDefSchema is a separate strict object the refinement cannot reach). S1 of the earlier record is closed.
  4. submitText (actions.md bullet, ReassignLeadAction example, eval 11, i18n _actions row) — RIGHT. An I18nLabel (actionSubmitText, 966-969), refused blank (SUBMIT_TEXT_BLANK), refused without a non-empty params (2272-2283), submitLabel / confirmLabel renamed onto it (1011), the dialog's default "Confirm" (useConsoleActionRuntime.tsx 345-349), the bundle slot objects.OBJECT._actions.ACTION.submitText with globalActions.ACTION.submitText for an object-less action (translation.zod.ts 221-224, 514, 1043). The example adds submitText: 'Reassign' to an action that already carries a one-entry params, type: 'api' and no confirmText, so the block still parses under the two co-constraints; check:skill-examples is CI's.
  5. The thank-you keys (i18n _views row) — RIGHT. _views.{view}.submitBehavior.title / .message, applied only where the form authors a kind: 'thank-you' panel; the three other kinds author no copy (translation.zod.ts 271-297, 336-337, 386-403). Not taught, and not asked by skills: list the form thank-you translation keys in the objectstack-i18n _views row #28215: the same shape's second address under pages.{page}.components.{id}.submitBehavior for a page object-form. Noted, no finding.
  6. kanban.lanes (list-views.md example and note, reworded on this head) — RIGHT. Ordered stored option values of groupByField (view.zod.ts 2357-2365); a choice of lanes, not a record filter, an unlisted value lands in Uncategorized, filter the view to keep records off the board, omit it for one lane per option in the options' own order (docblock 2321-2355; kanban-declared-lanes.ts 10-40); the two lint refusals (validate-list-view-field-refs.ts 1091-1112 unknown option, 1117-1141 repeated). The note now says "not one of the field's inline options", which is the judged population at 370-394 (a picklist-backed or option-less group field is not judged). S2 is closed.
  7. seriesBy (dashboards.md example and note, reworded on this head) — RIGHT. A second dataset dimension splitting the one yAxis measure into a series per value (report.zod.ts 33-57; DatasetReportRenderer.tsx 1375-1395 queries [xAxis, seriesBy] × [yAxis] and reads nothing from the report's columns); an undeclared name refused at the lint door exactly like xAxis (validate-chart-bindings.ts 589-604); refused when it names xAxis and on the single-series types (report.zod.ts 147-203); the five types the skill spells are SINGLE_SERIES_CHART_TYPES verbatim (dashboard.zod.ts 811-817: pie, donut, funnel, treemap, sankey); with it set series[].name names a value of the dimension (describe at 53-57; the lint stops judging series[].name against measures at 605-611). The replacement sentence "the chart's own split, where a second columns dimension only pivots the table" is what main does: a matrix report's columns is the table's across axis (ADR-0021 D2, the note two paragraphs up) and the chart's selection is xAxis / seriesBy / yAxis alone. The example's close_date is a declared dimension of the opportunity_metrics dataset the comment above the block names, with quarter granularity, and is not the xAxis, so the example passes both arms. S3 is closed.
  8. Eval 11 — RIGHT. The prompt needs the new key (a dialog whose button reads the verb is submitText and nothing else on main); must_contain is params plus submitText: 'Log Call'; must_not_contain is exactly the three spellings the spec refuses beside params (confirmText at 2251, submitLabel / confirmLabel at 1011), so a correct answer cannot be refused by the negative set. The single-quoted literal in must_contain is the fixture's standing convention (cases 1, 3, 9, 10 pin the same shape) in a single-quoted codebase.
  9. The eval fixture is measured, not ratcheted; the four ratcheted files, priced from the blobs at this head by the gate's own convention ceil(utf8 bytes / 4) (stamp line excluded on the SKILL.md), read 2256/2259 (actions.md, 9024 bytes), 3114/3129 (list-views.md, 12453 bytes), 6248/6262 (dashboards.md, 24992 bytes) and 5646/6333 (i18n SKILL.md, unchanged on this head) against the CEILINGS rows at scripts/check-skills-token-ratchet.mjs 825, 857, 846, 511 — no ceiling row moves, which is what the file list shows (the script is not in the diff). Reading, tree refs/review/pr-28488 fa3cef9. The check:skill-docs and check:skill-refs generators read SKILL.md FRONTMATTER and the zod mapping respectively (build-skill-docs.ts header; build-skill-references.ts header), so an i18n body edit owes no regenerated artifact and none is missing. scripts/role-word-baseline.json holds no row for actions.md and the role count is 0 before and after on every edited file.

② Semver level

.changeset/28243-skills-ui-i18n-teach-six-keys.md bumps @objectstack/skills alone at patch, and that is the one package the diff moves (the tree it ships is skills/**). The body and the PR body each carry the two line-initial declarations, the clause-② line reading no with no arm and the metadata-protocol line reading none: no key, value or metadata type is added, narrowed or re-meant — the keys taught all exist on main with the semantics the skill now states — so none takes patch by the Post-Task Checklist rule. No BREAKING banner, no ADR-0087 disposition and no migration is owed; none is written. The declared face is the rules face (skills/**, a governed register row), which is why this record exists at CONTRACT_REVIEW_TIER; Check Changeset on this head concluded success. RIGHT; the delta commit touches no changeset.

③ Boundary flags

The os-dev-report (comment 6550711618 on #28243) carries open_questions: [] and two out_of_scope_findings, both "none filed"; its other fields raise one process point. Each, answered:

Gate verdicts on this head at review time (38 check-runs): 17 success — Governed Surface Queue Guard, Build Core, Check Changeset, Check PR Size, PR-body guards, Dogfood Regression Gate, Plan Test Core shards, Test UI, Live E2E UI, Lint · PM dispatch-gates, the three Lint · scoped · lints, Type Check · workspace, Type Check · debt ledger, Auto Label, filter; 13 skipped by path filter (the ui/ lane: Lint UI, Type Check UI, Build & E2E UI, UI Published Artifacts, plus the opt-in harnesses and the schedule gate); 0 failures; and 8 still in_progress: Lint · ESLint, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · repo & tooling gates, Type Check · source gates, Type Check · consumer gates, Test Core (1/8) and Test Core (2/8). Those eight are not yet a pass (the same families all concluded success on 162680e, whose only delta to this head is prose in three rules files, but that is a forecast and not a verdict); the landing waits for them regardless of this record's verdict.

④ Design judgment

Judged on the tree, file by file, against the code named in ① and the cards' prompts; the delta commit's deletions judged phrase by phrase for a rule losing its only home and for re-wrapping to win room.

actions.md (−58 / +33 against main; the delta commit −5 / +5). Every deleted line was traced to its home:

  • The "Predicates are bare CEL" paragraph folds into the visible bullet: bare CEL, the record.-prefixed field form resolves identically on every surface, never ${…} or {…} wrapped, see objectstack-formula. The phrase "prefer it over the bare-field form" is dropped while its reason sentence is kept; the preference still reads from it. Kept.
  • The ctx section: the blessed-name code block becomes the same expression inline (ctx.user?.organizationId ?? ctx.session?.organizationId); the "two isolation axes / same undefined cases as hooks" text was already a pointer and on this head shortens again to "(same axes and undefined cases as hooks, objectstack-data)" — still a pointer to the skill that carries the table and the comment that the value equals organization_id / current_user.organizationId (data-hooks.md 362-363, 588-608); the trusted-execution rule, the ctx.session.positions absent-when-none fact and the ADR-0095 "not an authorization input" rule all survive in the one paragraph. Kept.
  • The "two mechanisms" lead-in, and on this head the closing "Use one of the two mechanisms above" of the params warning: the warning's rule ("Never express new-tab behavior via params — it is ActionParam[] for user input only") is intact, and the two remedies are the two subsections the warning sits directly under, named in the section heading (openIn / opensInNewTab / newTabUrl). The sentence was a pointer, not a rule. Kept.
  • The script example's lead-in "(collect params, then execute server body)" becomes "(params, then a server body)" — a caption; the example itself carries the shape. No rule.
  • openIn / opensInNewTab / newTabUrl prose: static vs computed URL, 'self', the omitted-key default (action.zod.ts 1202), synchronous pre-open, redirectUrl, the /-rooted path with {recordId}, external URL refused, valid only with opensInNewTab: true (1838-1844, 2363-2376), own-auth endpoint — every rule is in the condensed text. Kept.
  • The params warning drops "an object form params: { newTab: true } fails the zod build outright" and keeps the array-form trap and the rule itself. The object reading is retired on the spec side (1426) and refused with its own prescription, so the dropped sentence had the schema as its home. Kept in substance.
  • "Always" / "Best practice" lead-ins trimmed; refreshAfter: true stays as the closing rule. Kept.
  • Re-wrapping: no hunk on either head is a re-wrap for budget — each changed paragraph is rewritten and the byte deltas come from the rewrites. The delta commit shortens one line of the ctx paragraph and leaves the next line short ("Bodies execute trusted (ctx.api /"); that is an un-rewrapped seam, cosmetic, no bytes won.

New sentences and examples: judged in ① items 1-4; all key names, value shapes, defaults, scopes and refusal spellings match main.

list-views.md (−10 / +8 against main; the delta commit −1 / +1). The three deleted bullets (toggle deprecated per ADR-0047; the switcher is a compact dropdown and a single-entry whitelist locks it; groupByField is a select, one column per option) become the code comments beside element, allowedVisualizations and groupByField — the rule each carried survives at the spot an author copies. Lost with them: the "§3.4a" citation and "stays in the enum for back-compat rendering", neither an authoring rule; the later Rules bullet still says the full dropdown | tabs | toggle range is page-only, as before. The lanes example lists three option values of status in an order the options' own order does not dictate, which is exactly the key's reason for existing. The delta commit changes only the refusal sentence (① item 6); no deletion.

dashboards.md (−8 / +7 against main; the delta commit −4 / +4). The "Engine support" bullet (per-driver bucket functions) describes the engine, not what an author writes, and keeps a home at skills/objectstack-query/rules/aggregation.md 82; the "Studio's Dashboard Widget Inspector" bullet describes Studio and tells an author nothing to write. The bucket-label table and "Human labels are automatic" keep the author-facing fact. The delta commit's two deletions: "— the Looker / Power BI "see records → open page" model" is a vendor analogy with no rule in it (the escape-hatch behaviour it decorated is stated in full in the same bullet); "Dashboard and report drill are unified." restates the sentence that opens its own paragraph ("Reports drill the same way … opens the identical in-place drawer"). Neither was a rule's only home. The seriesBy note's three lines are re-broken because its content changed (① item 7), not to win room — the paragraph grew by a line.

views-apps-actions-pages.json (+10). Judged in ① item 8; valid JSON, the same shape as cases 1-10. Unchanged on this head.

objectstack-i18n/SKILL.md (−2 / +2). Judged in ① items 4 and 5; 687 tokens of headroom, no deletion needed. Unchanged on this head.

Findings:

  • S1, S2, S3 of the record on 162680e — RESOLVED on this head, each checked against the code in ① items 3, 6 and 7.
  • S4 (should-fix, cosmetic, left by the seat) — actions.md ctx paragraph: a ~125-character first line, and now a short seam line after the shortened cross-reference. No bytes, no rule, no verdict weight.

Must-fix: none. Nothing the PR teaches contradicts main; no deleted phrase on either head was the only home of a rule, and no line was re-wrapped to buy ceiling room.

Implemented-by: claude/project-thread-0tmmio
Reviewed-by: session_01L9qibzCtJt8Q2Z63fyRNix

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-tesla · session_01L9qibzCtJt8Q2Z63fyRNix

维护者速读

  • 改了什么:objectstack-ui 和 objectstack-i18n 两个技能补教六个已经上线的键,一张卡一个 commit。动作规则:pressed(记录页头把动作画成「已按下」,只做显示,不做权限)、五个文案占位符槽位都用 {{ }}(新增 description、参数的 helpText、outcomeMessages)、参数对话框按钮文字 submitText(替代默认的「Confirm」)。列表视图:看板 kanban.lanes(按顺序列出要画的泳道)。报表:图表 seriesBy(按第二个维度拆成多条序列)。i18n:submitText 的翻译槽位,以及表单「感谢页」的 submitBehavior.title / .message 翻译键。另加一道测试题(第 11 题),考 submitText。
  • 为什么改:六个键的渲染都已合入,技能还没教,AI 写元数据时不会用,或者写成会被拒的拼写(submitLabel、${…} 之类)。
  • 风险与回滚:只改技能文本,不碰运行时。为了守住 token 上限,删掉了一些重复或只讲引擎、Studio 的句子,复审逐句确认没有规则因此丢失。回滚即 revert。
  • 先看:skills/objectstack-ui/rules/actions.md 的 pressed、submitText 和文案占位符几条。
  • 两分钟验证:git grep -n -E "pressed|submitText|seriesBy|lanes:" skills/objectstack-ui/rules/。
  • 需要你做的:改的是 skills/**,属于 Tier H,需要 os-zhuang(或 hotlong)批准后才能合并。契约复审 PASS 记录 6551037778,Head-sha fa3cef9c。

席位意见:契约复审 PASS。第一轮提了三条措辞建议(confirmText 的拒收只针对注册的动作;看板泳道只按字段自带的选项判;seriesBy 那句「不是 matrix 的变通」容易误读),dev 已在新 head 上改好,复审重新确认,删掉的句子都不是某条规则唯一的出处。可以批准。

席位核对

ACCEPT · seat domain:skills#1 · head fa3cef9c · 2026-11-01T00:21Z

  1. Shape: six Fixes lines (skills: teach action.pressed in the objectstack-ui actions rule #28243 Skills: action copy hole list names the three new template slots (description, param helpText, outcomeMessages) #28464 skills(objectstack-ui, objectstack-i18n): teach the action param dialog's submitText (button verb instead of Confirm) and its translation slot #28190 skills: list the form thank-you translation keys in the objectstack-i18n _views row #28215 skills: teach kanban.lanes in the objectstack-ui list-views rule #28212 skills: teach report chart seriesBy in the objectstack-ui dashboards rule #28207), draft, base main, 6 files (actions / list-views / dashboards rules, the views-apps-actions-pages eval, objectstack-i18n SKILL.md, a patch changeset for @objectstack/skills), +75/−81, one commit per card plus a role-word fix-up and the should-fix commit. No spec or code path.
  2. Ruling: none on any member; triage's routes as written; p3 taken under the maintainer's 「p3 也可以开发」 (2026-10-30). No member dropped.
  3. Contract review: PASS 6551037778 at this head (supersedes 6550826945 at 162680ed; S1–S3 applied and verified, S4 cosmetic left). Clause-②: no, Metadata-protocol: none.
  4. Gates / check-runs: dev report 6550711618; at fa3cef9c all 45 derived gates exit 0, --ran 45/45, check:skill-examples exit 0 (out/28243-gates-ran-fa3cef9c.list). Token readings 2256/2259, 3114/3129, 6248/6262, 5646/6333. On head fa3cef9c: 41 check-runs, 28 success, 13 skipped, 0 failed; mergeable_state clean.
  5. Accepted deviations: the PR body's verification section still names 162680ed (the dev writes the body once; this record carries the head). S4 (one long line in the ctx paragraph) left as cosmetic.
  6. Landing: check-governed-merges --pr 28488 reports GOVERNED, Tier H (skills/**). The seat readies the PR and the relay's governed-review leg requests the approvers. No auto-merge is armed by the seat. After an authorized approval, the seat lands it, or the approver enqueues it.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 1, 2026 00:22
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit 3b45d4a Nov 1, 2026
42 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-0tmmio branch November 1, 2026 03:54
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 size/m tooling

Projects

None yet

2 participants