skills(objectstack-data): the tree rows state the landed reference contract - #16187
skills(objectstack-data): the tree rows state the landed reference contract#16187claude[bot] wants to merge 1 commit into
tree rows state the landed reference contract#16187Conversation
…` contract `refuseForeignTreeReference` (packages/spec/src/data/object.zod.ts) makes a `tree` field's `reference` optional and, when written, equal to the declaring object's own name — the `extend` target on an extension. Any other object is refused at parse with one issue at `fields.<field>.reference`. The published skill still listed `reference` as a bare key prop, so a client agent reading it had no way to know either half of the rule. Two rows, no new section: - `rules/field-types.md` — the Relational table's `tree` row now states optional-and-self-only and that a foreign object is refused at parse. - `rules/relationships.md` — the `tree` row's bare "Optional" cell now says what is optional and contrasts the refusal with the `master_detail` row's lint-warned cell in the same column. The token ratchet left `field-types.md` 27 bytes under its ceiling, so the row is paid for inside the same file: the Media section's "no per-field attachment config" note restated the top-of-file blockquote, which already refuses `fileAttachmentConfig` by name, above every table. That file now shrinks. The `Hierarchical: tree (Self-Reference)` example is unchanged — it already writes the object's own name (`reference: 'category'` on `category`). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
|
ACCEPT — governed in-seat review of PR #16187, head Implemented-by: Verified in a detached compare worktree at the PR head, not from the report:
Ruling on the open question — A. +21 tokens at package level is accepted: the per-file ratchet is the gate, both files are under ceiling, and the ruling behind the ratchet targets padding a small feature into prose, which two contract sentences are not. B (an unrelated 84-byte cut to reach net zero) would be a rider on a governed surface. C is queued: the seat files the dev's reported finding (the ~1800-byte ASCII decision tree in Landing regime: governed ( Generated by Claude Code |
维护者速读事情:发布给客户 agent 的 改动:两行表格,不加新章节—— 请您看一眼的是那处删除:token 棘轮把 没动的地方: 治理面: 席位意见:建议合并。整包 +21 tokens 席位已裁为可接受(按文件棘轮是门,两个文件都在顶下)。顺带一条已立卡、不在本 PR 里的事: Generated by Claude Code |
Fixes #16083
refuseForeignTreeReference(packages/spec/src/data/object.zod.ts, onmainsincefb77aa541) makes atreefield'sreferenceoptional, and — when written — equal to the declaring object's ownname(theextendtarget on an extension). Any other object is refused at parse with onecustomissue atfields.FIELD.reference. The publishedobjectstack-dataskill still listedreferenceas a bare key prop, so a client agent loading the skill had no way to know either half of the rule.content/docsalready says it; these two rows were the last surface that did not.Two rows, no new section
rules/field-types.md(Relational table)`reference``reference`— optional; when written, this object's own name (any other object is refused at parse)rules/relationships.md(Relationship Types table)Requiredcell was a bareOptionalmaster_detailrow's "lint-warned" in that same columnWording is taken from the refusal message and from the already-landed
content/docssentences (validation-rules.mdx:297,field-type-decision-tree.mdx:183), not re-invented — the card's acceptance asks for the words the spec's refusal uses.Unchanged on purpose: the
Hierarchical: tree (Self-Reference)example. It already writes the object's own name (reference: 'category'on objectcategory), which is all the ruling asks of it. I drafted a clarifying comment on that line and then dropped it: the row three screens up in the same file now states the rule, so the comment would have cost 28 bytes without changing what a client agent writes.Ratchet accounting — and the deletion that paid for it
The token ratchet left
field-types.md27 bytes under its ceiling, and the shortest faithful clause for that row is ~88 bytes. The deletion is therefore compelled by the ratchet, not elective tidying.What was spent: the Media section's two-line note "There is no per-field attachment config (size limits, allowed types, storage) — storage concerns live outside the field schema." It restates the top-of-file blockquote, which sits above every table and already refuses the exact key by name:
grepconfirms the note appears nowhere else inskills/. Removing it changes nothing a client agent writes; the operative refusal and the exact invented key both survive.rules/field-types.mdrules/relationships.mdfield-types.mdshrinks. The package moves +21 tokens net (140376 → 140397 of a 157650 ceiling) — stated plainly rather than rounded away, and paid for inside the two files the ruling names.Gates — union re-run on the final commit
5a30844272node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 20 families from the real changeset; all 20 run, reconciled with--ran:✓ dispatch-gates --ran: 20 derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED.Every exit code was captured by redirecting first, never through a pipe.
✓ check-skills-token-ratchet: 36 authored bundle file(s) within their ceilings— plus✓ … self-test: 64 cases pass.check-nul-bytes: OK (scanned 7932 text file(s) … no raw ASCII control bytes)pnpm lint(eslint . --no-inline-config) — exit 0, not narrowed. It ran on this commit's exact tree (git diff HEADandgit status --porcelainboth empty since).check:pm-governed-merges,check:skill-frame-sync,check:skill-compatibility,check:skill-identifier-liveness,check:doc-authoring,check-published-list-mirrors(+ self-test) — all exit 0.check-governed-merges --teston both paths — exit 3, GOVERNED, as expected:skills/** ×2 — the published skills catalog.PREREQUISITE NOT MET(exit 3, nothing measured) because@objectstack/formulawas unbuilt; afterturbo run build,check:doc-formula-expressionsis exit 0.Two gates named in the brief,
check-doc-snippet-typesandcheck-skill-examples, do not exist in this repo — no script, no package.json entry.check-skill-id-lintexists but its corpus is.claude/skills/pm-dispatchplus.claude/agents/os-dev.mdandAGENTS.md; the publishedskills/**roots are not in it, so it does not read these files. Run anyway: exit 0.No changeset, and
skip-changesetis applied:skills/is not a workspace package and nopackage.jsonfilesarray names it, so this diff releases nothing. Six landedskills/**-only commits carry zero changesets.维护者速读(草稿)
发布给客户 agent 的
objectstack-data技能里,tree字段的reference一直只是裸列的一个键。契约那半边早已落地——写别的对象在 parse 就被拒——但技能文本没说。客户拿我们的 AI 写元数据 app 时,读到的是「tree 要配 reference」,于是写出指向另一个对象的 tree,一路到 publish 才被拒。这是沉默造成的返工,不是错误陈述。改动是两行表格,不加新章节:
field-types.md的 Relational 表tree行,和relationships.md关系类型表的tree行。措辞直接取自 spec 的拒收信封,以及content/docs上个 PR 已经落地的同一句话,没有另造一套说法——分叉的措辞就是下一张卡。要请您看一眼的是那处删除。token 棘轮把
field-types.md卡在离天花板 27 字节,而把契约在那一行说清楚最少要 88 字节:删是被棘轮逼出来的,不是顺手清理。删掉的是 Media 段那句「没有 per-field attachment config」——它复述的正是文件开头那段引用块,而那段已经点名拒收fileAttachmentConfig,位置还在所有表格之上。删完该文件净缩。没动的地方也请留意:
tree的示例一个字没改,它本来就写着对象自己的名字。示例行上我起过一句注释,又拿掉了——同一文件的表格行已经把规则说了,再写一遍只是多花字节,不改变客户 agent 写出来的东西。治理面:
skills/**在名册上,所以这是 draft PR、人工合并,席位不 flip ready、不入队、不 approve。纯散文改动,无契约移动,无 changeset。整包净增 21 tokens,已如实写在上面的表里。席位意见:
Generated by Claude Code