Repository navigation
Commit 253eab0
docs(skills): the data skill's Field Groups section teaches a group's visibleWhen and requiresFeature (#30890)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoFLj7Er8ys8HFHE35FnUcar)_
domain:skills#2 · os-tesla · session_01L9qibxLYNwSMSVQkj55gA6
Fixes #30812
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`skills/objectstack-data/references/examples-objects.md` 的 Field
Groups 一节新增一条:整个字段分组(含标题)可以用 `visibleWhen`(对记录求值的 CEL,为 FALSE 或求值出错即隐藏)或
`requiresFeature`(取自 `PUBLIC_AUTH_FEATURES` 的部署特性名,未知名字解析即拒绝;解析时降成该分组的
`visibleWhen` 并去掉,与作者自写的 `visibleWhen` 以 AND 合成、作者谓词在前)隐藏,两者都只管界面、字段仍可经
API 读取;示例取真实的 `sys_organization`「集团结构」分组(`requiresFeature:
'multiOrgEnabled'`)。
- 为什么改:PR #30810 给字段分组加了 `requiresFeature`,技能文本仍只列
`icon`、`description`、`collapse`,读技能的 agent
写对象时不知道一个分组可以按谓词或按部署特性隐藏,会去改表单页面或者什么都不做。
- 风险与回滚:只改技能文本、一条 changeset 和 token 棘轮的一行(1756 → 1737,向下重锁到合并后的实测值:本 PR
削 5,已合并的 #30831 削 14 且未重锁);运行时零改动,行文逐词对照 `main` 上的 `object.zod.ts` 与
`public-auth-features.ts`。回滚即 revert。
- 先看:`skills/objectstack-data/references/examples-objects.md` 的 Field
Groups 段(新增一条 + 四处删减);同文件 Quick-Start 的一行注释与 Metadata Protection
的一句删减;`scripts/check-skills-token-ratchet.mjs` 的 examples-objects 行。
- 两分钟验证:`node scripts/check-skills-token-ratchet.mjs | grep
examples-objects`,预期 `is 1737 tokens (ceiling 1737; headroom 0)`;再对照
`packages/spec/src/data/object.zod.ts` 中 `requiresFeature` 的 describe
文本,技能句子与之一致。
- 需要你做的:本 PR 触 `skills/**`(Tier H),需要受管审批人在 GitHub 上 Approve 一次;之后席位落地。
## What changes
`skills/objectstack-data/references/examples-objects.md`, section "Field
Groups (MVP)": one new bullet after the per-group options, stating what
`main` does since PR #30810 (`ObjectFieldGroupSchema` in
`packages/spec/src/data/object.zod.ts`, `lowerRequiresFeature` in
`packages/spec/src/kernel/public-auth-features.ts`):
- a whole group, header included, is hidden by `visibleWhen` (CEL over
`record.*`; FALSE or a fault hides it — the slot is fail-closed) or by
`requiresFeature` (a deployment feature flag from
`PUBLIC_AUTH_FEATURES`; an unknown flag is refused at parse);
- `requiresFeature` is lowered at parse into the group's `visibleWhen`
and stripped from the output, AND-combined after an authored
`visibleWhen` (the author's predicate first);
- both are presentation only: the fields stay readable through the API;
- the example is the real one: `sys_organization` hides Parent
Organization and Sort Order on a single-organization deployment with `{
key: 'group_structure', label: 'Group Structure', requiresFeature:
'multiOrgEnabled' }`
(`packages/platform-objects/src/identity/sys-organization.object.ts`).
Not taught, on purpose: the gate's CEL spelling (`features.X != false` /
`== true`), the four composition refusals (`visibleWhen: false`, a
non-CEL or AST-only predicate, a blank `source`), the lint's
accepted-trailing-gate rule, and the console's empty-`features`
pre-fetch window. The skill names the two keys and the one behaviour an
author must know; the rest is the schema's own docblock and refusal
text.
**Growth paid in the same file, by deletion (no re-wrap):**
| Deleted | Why it was restatement |
|:--|:--|
| The section's intro sentence (`Organize fields into logical groups
(e.g., "Contact Information", "Billing", "System") for forms, detail
pages, and editors.`) | The heading says what the section is; the
example right below shows those three labels; the `collapse` bullet
already says groups render on forms, modals and detail pages. |
| `; group labels are human-readable` | A truism beside the `label:
'Contact Information'` lines of the example. |
| `— replaces the deprecated \`defaultExpanded\` flag, ADR-0085` |
History of the `collapse` enum, not a rule; the enum's three values stay
spelled out. |
| The "Supported migrations at this layer" paragraph | Restates bullets
1 and 2 (edit the `fieldGroups` array; edit `Field.group`); its last
sentence was a roadmap note. |
| `— see above` in the Quick-Start `sharingModel` comment | A dangling
pointer: the text it pointed at lives in `SKILL.md`, not in this file
(the block was moved here verbatim). `// required in practice` stays. |
| `The block is \`.strict()\`: \`reason\` is **required** (min 1 / max
500 chars) and unknown keys are rejected.` (Metadata Protection) | The
schema block two lines above already says `REQUIRED — reason … (1–500
chars)`; strictness is taught once in `SKILL.md`
(`ObjectSchema.create()` rejects unknown keys). |
| `// src/objects/sys-user.object.ts` (Metadata Protection example) | A
path comment the other two `os:check` blocks in this file do not carry.
|
`scripts/check-skills-token-ratchet.mjs`: the `examples-objects.md` row
re-locks DOWN at the landed count, 1756 → 1737, zero headroom, with the
usual comment: -5 from this PR's cut, -14 from the merged #30831 (its
hook-example rename dropped the block's intro line and left the headroom
unlocked).
`.changeset/30812-field-group-visible-when-requires-feature.md`:
`'@objectstack/skills': patch`, both declaration lines.
No eval case: the `objectstack-data` evals convention
(`skills/objectstack-data/evals/README.md`) lists the two fixtures'
topic clusters and binds no taught key to a case; the card asked for one
only if the convention asks. The seat may add one if it reads the
convention wider.
**Readings (os-dev rule for `skills/**`):**
| Reading | Before (`origin/main` `8b9cc712`) | After (`62fd849b`) |
|:--|:--|:--|
| `skills/objectstack-data/references/examples-objects.md` lines | 195 |
191 |
| `skills/objectstack-data/references/examples-objects.md` bytes /
tokens (`check-skills-token-ratchet`, `ceil(bytes/4)`) | 6964 / 1741
(ceiling 1756, headroom 15) | 6945 / 1737 (ceiling 1737, headroom 0) |
| Package, all 11 `skills/*/SKILL.md`, lines | 4525 | 4525 |
| Package, all 11 `skills/*/SKILL.md`, tokens (gate formula) | 56670 |
56670 |
## Why
#30812, the `Skills-docs: follow-up` PR #30810 declared (card #30692,
ruling 6670372892 row 4 A). The key exists on `main` since that merge;
until the skill names it, an agent reading the Field Groups section
learns only `icon`, `description` and `collapse`, and either leaves a
feature-bound section visible everywhere or reaches for a page layout to
hide it.
## Tests
No runtime code changes. Every word of the new bullet was read off
`main` at `a2f523aa` (unchanged at `8b9cc712`):
`ObjectFieldGroupSchema`'s `visibleWhen` / `requiresFeature` docblocks
and `.describe()` texts, `lowerRequiresFeature` (`'visibleWhen'` slot:
gate alone when no predicate is authored, `(AUTHORED) && GATE`
otherwise), `z.enum(PUBLIC_AUTH_FEATURE_NAMES)` (unknown flag refused),
and `sys_organization.object.ts` line 352 (the `group_structure` entry
quoted verbatim). The pins behind them are
`packages/spec/src/data/field-group-requires-feature.test.ts` and
`packages/platform-objects/src/identity/sys-organization-group-structure-gate.test.ts`,
unchanged here.
`pnpm --filter @objectstack/spec check:skill-examples` (the three
`os:check` blocks of this file among the 283 marked blocks) against
freshly built `@objectstack/spec`, `@objectstack/client` and
`@objectstack/client-react` declarations: `✅ 283 prose examples
type-check across 3 surface(s)`, exit 0 — at `36152cd8` and again at the
head `62fd849b`.
## Acceptance notes
- Overlap: seat 1's PR #30831 (merged as `0e73de82` while this card was
open) edits this file's Lifecycle Hooks block (hook name and its intro
line); this diff stays inside the Field Groups section, the Quick-Start
comment and Metadata Protection. `origin/main` `8b9cc712` was merged in
(`909ea5ea`) with no conflict; the ratchet row was then re-locked at the
merged count. `skills/objectstack-data/rules/field-types.md` (#30824) is
untouched.
- The title line `# Data — object authoring examples (moved verbatim
from SKILL.md)` is left as is: the same convention heads four reference
files, so changing one is a catalog decision, not this card's.
- `check:skill-examples` refuses (exit 3, PREREQUISITE NOT MET) until
`packages/client` and `packages/client-react` carry `dist/*.d.ts`; a
spec-only build is not enough for it.
## Verification
Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` against the three-file diff (no paths
passed; the tool reads the change set off the merge base). Every line
run with stdin closed, exit code landed to disk before any pipe
(`COMMAND :: exit CODE`):
- At `36152cd8` (before any `origin/main` merge; spec, client,
client-react and lint built): 62 derived families, 62 run, 61 exit 0, 1
exit 3 (`pnpm check:dual-build-cjs-loads`, PREREQUISITE NOT MET: 31
packages had no `dist/`); `--ran`: 62 derived, 61 run, 1 NOT-MEASURED
(derived from the recorded exit 3), 0 UNRUN.
- At `c5cc868a` (after merging `origin/main` `18870c92` and a
declarations build of every `./packages/**`): the same 62 families, 62
run, 62 exit 0; `--ran`: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN.
- At `62fd849b` (the head: `origin/main` `8b9cc712` merged, the ratchet
row re-locked at 1737): the same 62 families, 62 run, 62 exit 0;
`--ran`: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. Ran lists:
`skills-seat-2/out/30812-gates-ran-{36152cd,c5cc868a,62fd849b}.list` in
the project files.
<details><summary>Gate readings at 62fd849</summary>
- `check-skills-token-ratchet`:
`skills/objectstack-data/references/examples-objects.md is 1737 tokens
(ceiling 1737; headroom 0)`, exit 0; `--self-test`: 97 cases pass, exit
0.
- `check:skill-identifier-liveness`: `Leg 1: 486 citation(s) over 71
published file(s) … Leg 2: 8 registered exhaustive section(s), 0
ledgered gap(s)`, exit 0.
- `check:dual-build-cjs-loads`: `116 published require entry point(s)
across 71 package(s) load; 2456 emitted CommonJS file(s) parse`, exit 0
(declarations build of `./packages/**`; exit 3 without it).
- `check-empty-changeset --base origin/main`: `1 declaring changeset(s)
added`, no merge-base changeset modified, exit 0.
`check-adr-0087-registration --base origin/main`: no declared-breaking
changeset, exit 0. `check-changeset-no-major --base origin/main`: exit
0.
- `check:doc-authoring`: `21232 customer-facing string(s) across 1711
spec sources clean`, exit 0.
- `pnpm --filter @objectstack/spec run check:skill-refs`,
`check:skill-compatibility`, `check:skill-frame-sync`,
`check:pm-skill-id-lint`, `check:pm-governed-merges`,
`check:lean-entry-closure`, `check:nul-bytes`, `check:role-word`,
`check-comment-mask-corpus`, `check:pm-dispatch-gates` and the rest of
the 62: exit 0.
</details>
Tier: `dispatch-gates --tier` on the three paths: `skills/**` clause ①,
`claude-fable-5-1` mandated, no one-line exit; this thread runs on it.
`check-governed-merges --test` on the three paths: Tier H
(`skills/objectstack-data/references/examples-objects.md`).
CI families (`Lint & Repo Gates`, `Test Core`, the rest) are the PR's
check-runs, not re-run locally. Dogfood battery: n/a (no `!` in the
title, no narrowing arm).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's run (no source file in
the diff).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` not touched.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibxLYNwSMSVQkj55gA6
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 1c34fd7 commit 253eab0
3 files changed
Lines changed: 32 additions & 18 deletions
File tree
- .changeset
- scripts
- skills/objectstack-data/references
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1012 | 1012 | | |
1013 | 1013 | | |
1014 | 1014 | | |
1015 | | - | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
1016 | 1024 | | |
1017 | 1025 | | |
1018 | 1026 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | 5 | | |
9 | 6 | | |
10 | 7 | | |
11 | 8 | | |
12 | | - | |
| 9 | + | |
13 | 10 | | |
14 | 11 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
19 | 23 | | |
20 | 24 | | |
21 | 25 | | |
| |||
44 | 48 | | |
45 | 49 | | |
46 | 50 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| |||
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | 161 | | |
165 | 162 | | |
166 | 163 | | |
| |||
172 | 169 | | |
173 | 170 | | |
174 | 171 | | |
175 | | - | |
176 | 172 | | |
177 | 173 | | |
178 | 174 | | |
| |||
0 commit comments