Repository navigation
Commit 33bc8ae
docs(skills,spec): the fieldPatches example patches options only on a field that declares them, so HotCRM accepts it (#30995)
<!-- claude-projects-attribution: {"github_login":"huangyiirene"} -->
_Requested by **Irene** · [project
thread](https://claude.ai/code/project/chan_01TJXtQ1tVD26ecLDZxFS67x?thread=cmsg_01TJXtQ1tVD26ecLDZxFS67xMHYuqFd4hLYHNf14Fg8wBe)_
Fixes #30944
Clause-②: no
Metadata-protocol: none
`dispatch-gates` mandates the contract-review model tier for `skills/**`
(clause ①). The maintainer's standing order in this project (2026-11-03)
waives it; this PR was developed below that tier, and the dispatching
seat runs the contract review.
## 维护者速读
- 改了什么:`fieldPatches` 的唯一示例改为只改 `industry` 的 `label`,选项补丁移到自带 inline
`options` 的 `type`(`customer` → `Client`);覆盖 `objectstack-data` 技能、spec
的 `fieldPatchesDescription()` 与
TSDoc、`object-extensions.mdx`,`references/data/object.mdx` 由 spec 重新生成。
- 为什么改:HotCRM 的 `crm_account.industry` 绑定共享 picklist
`industry`,`fieldPatchTargetMisses` 对其任何 `options` 键返回
`picklist-option`,照抄旧示例会在安装和 `os validate` 时被拒。
- 风险与回滚:只改说明文字和示例,无 schema、键或行为变化;发布 `@objectstack/skills` 与
`@objectstack/spec` 各一个 patch;回滚即 revert。
- Token 账:SKILL.md 示例多 10 字节,删掉同文件 `.custom` 段末重复的 "a base field changes
only through `fieldPatches`" 付账;上限行不动(6068/6078)。
- 先看:`skills/objectstack-data/SKILL.md` L364 与
`packages/spec/src/data/object.zod.ts` `fieldPatchesDescription()`。
- 需要你做的:无;席位负责复审与落地。
## What changes
- `skills/objectstack-data/SKILL.md` L364: the example is now
`defineObjectExtension({ extend: 'crm_account', fieldPatches: {
industry: { label: 'Sector' }, type: { options: { customer: { label:
'Client' } } } } })`. `extend: 'crm_account'` and `label: 'Sector'` stay
whole tokens (eval cases 5 and 7 of
`evals/objects-fields-relationships.json` require them). The sentence
before it still states that "a `picklist`-bound field's options are
never patchable".
- `packages/spec/src/data/object.zod.ts`, description text only:
`fieldPatchesDescription()` now says an option of a field bound to a
shared `picklist` is never patchable and only a field declaring its own
`options` takes an option patch, and its example sets `industry`'s
`label` and `inlineHelpText` and patches `type`'s `customer` option. The
`ObjectExtensionSchema` TSDoc example carries the same patch; since that
example extends `contact`, its comment is conditional: where `industry`
is picklist-bound, as on HotCRM's `crm_account`, its options are not
patchable (its display keys are). No schema change.
- `content/docs/references/data/object.mdx`: regenerated with `pnpm
--filter @objectstack/spec gen:docs` after `pnpm --filter
@objectstack/spec build`; not hand-edited.
- `content/docs/data-modeling/object-extensions.mdx` L104-118: the
`os:check` example drops `industry`'s `options` (a comment says it takes
them from a shared picklist) and patches `type: { options: { customer: {
label: 'Client' } } }`. Its existing bullet on picklist-bound fields
(L152-153) now agrees with the example.
- Two changesets: `'@objectstack/skills': patch` and
`'@objectstack/spec': patch`, each with `Clause-②: no` and
`Metadata-protocol: none`. No `skip-changeset`.
Read on HotCRM `main` at `6961ab1`,
`src/sales/objects/account.object.ts`:
- L311-318: `industry: Field.select({ label: 'Industry', group: 'basic',
picklist: 'industry' })`, no inline `options` (the values, `technology`
among them, live in `src/sales/picklists/industry.picklist.ts` L23).
- L300-309: `type: Field.select({ label: 'Account Type', …, options:
[...] })` declares its own inline options; L305 is `{ label: 'Customer',
value: 'customer', … }`, so `customer` is a stored value the patch can
address.
Read on objectstack `main`: `fieldPatchTargetMisses` in
`packages/metadata-core/src/extension-field-patch-targets.ts` pushes
`reason: 'picklist-option'` for every key under a patch's `options` when
the target field has a non-empty `picklist`, before it looks at option
values; a field with inline `options` is checked by stored value
(`reason: 'option'` when absent). The new examples pass both checks.
Out of scope and left as is: the `options` shape error message in
`object.zod.ts` (`'options: { technology: { label: \'Tech\' } }'`, near
L5034) illustrates the map shape, not a HotCRM patch.
**Token ratchet.** `skills/objectstack-data/SKILL.md`: after merging
`main` at `acd190b2` the ceiling is 6078 with 1 byte of slack. The new
example adds 10 bytes. Payment, by the seat's ruling: the `.custom`
paragraph's closing clause "; a base field changes only through
`fieldPatches`" (L372 on `acd190b2`) is deleted. Its home is L355-360 of
the same file: "**Relabel a field the owner declares with `fieldPatches`
— never by redeclaring it in `fields`**" and "the type, values and rules
stay the owner's". The file is now 6068 tokens (ceiling 6078, headroom
10). No ratchet row moves, nothing was re-wrapped. Net lines in
`skills/**`: 0.
## Why
The one concrete `fieldPatches` example the platform taught, in the
skill, the spec description and two docs pages, patched an option of
HotCRM's picklist-bound `industry`. A verbatim copy was refused at
install and by `os validate`, and the skill paragraph contradicted its
own rule.
## Verification
Head `5a6e8fad`: the two edit commits, a merge of `origin/main` at
`acd190b2` (`8920b06e`, no conflict, no rebase), the ratchet-payment
commit, and one commit for the contract review's two should-fixes (TSDoc
comment made conditional; a skills-changeset claim the skill text does
not make, dropped), and one commit for the delta review's must-fix (the
comment said only the label was patchable; a picklist binding blocks
only option patches).
<details><summary>Local gates on 7c661de (head 5a6e8fa changes one
TSDoc comment only): 105 derived, 105 run with exit 0, 0 NOT MEASURED
(`dispatch-gates --ran`)</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 105 commands for the 6 changed
files. All ran after `pnpm install --frozen-lockfile`, a full `pnpm
build` of the merged tree and a `@objectstack/spec` rebuild on this head
(each exit 0), each under a 900 s timeout with
`NODE_OPTIONS=--max-old-space-size=8192` and its exit code captured
before any pipe. The larger heap is for this container: at the script's
default 4096 MB, `check:test-typecheck` inside `check:generated` had tsc
exit with no diagnostics; at 10240 MB it was green.
- All 105 exited 0, among them `check-skills-token-ratchet.mjs`,
`check:generated`, `check:docs`, `check:skill-examples`,
`check:skill-identifier-liveness`, `check:doc-authoring`,
`check:dts-closure`, `check:dual-build-cjs-loads` and
`check-comment-mask-corpus.mjs`. Also run: `pnpm --filter
@objectstack/spec exec tsc --noEmit` (exit 0) and `pnpm exec eslint
packages/spec/src/data/object.zod.ts` (exit 0).
- `--ran` printed: `105 derived famil(ies) accounted for — 105 run, 0
NOT-MEASURED (a DERIVED zero — all 105 recorded an exit code and none of
them is 3)`.
- The first head `ad4d96f6` (before the merge) also ran all 105 with
exit 0; there `check-plugin-teardown-shape.mjs --self-test` first exited
3 because the shallow clone lacked its pinned fixture commit, and exited
0 after fetching it.
- `node scripts/check-commit-card-trailers.mjs --range
origin/main..HEAD`: exit 0 (6 commits) before each push. Heads
`22db7aee` and `7c661dec` ran all 105 with exit 0; `5a6e8fad` changes
only those two TSDoc comment lines, and on it `pnpm --filter
@objectstack/spec build` and `check:generated` exit 0 with nothing
stale.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(Description text only.)
- [x] 2. Every name I added uses the casing for its kind. (No names
added.)
- [x] 3. No formatter ran. `eslint` on the touched TypeScript file exits
0.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request. (`main` merged in, no rebase.)
- [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` touched: its generated artifacts are
regenerated and committed (`check:generated` exit 0).
- [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. (The 维护者速读 section is
Chinese, per the lane template.)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J8VVAmsHMwmra6cg78RoEp
<!-- signed: domain:skills#3 · huangyiirene ·
session_01J8VVAj7CvjkGXvgXzd6RZ7 -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent dc3adf3 commit 33bc8ae
6 files changed
Lines changed: 34 additions & 7 deletions
File tree
- .changeset
- content/docs
- data-modeling
- references/data
- packages/spec/src/data
- skills/objectstack-data
| 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 | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
114 | 117 | | |
115 | 118 | | |
116 | 119 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
580 | 580 | | |
581 | 581 | | |
582 | 582 | | |
583 | | - | |
| 583 | + | |
584 | 584 | | |
585 | 585 | | |
586 | 586 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5152 | 5152 | | |
5153 | 5153 | | |
5154 | 5154 | | |
5155 | | - | |
5156 | | - | |
| 5155 | + | |
| 5156 | + | |
| 5157 | + | |
| 5158 | + | |
5157 | 5159 | | |
5158 | 5160 | | |
5159 | 5161 | | |
| |||
5168 | 5170 | | |
5169 | 5171 | | |
5170 | 5172 | | |
5171 | | - | |
| 5173 | + | |
| 5174 | + | |
| 5175 | + | |
5172 | 5176 | | |
5173 | 5177 | | |
5174 | 5178 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
361 | 361 | | |
362 | 362 | | |
363 | 363 | | |
364 | | - | |
| 364 | + | |
365 | 365 | | |
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
372 | | - | |
| 372 | + | |
373 | 373 | | |
374 | 374 | | |
375 | 375 | | |
| |||
0 commit comments