Repository navigation
fix(lint)!: a field group's visibleWhen is judged like a field's own, at build and at the object save door - #26928
Conversation
…ject door A field group's section predicate now takes the field visibleWhen slot's verdict (expression-invalid), located at the group's key, in os validate / build and at the object save door. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF
…ugh the real save door Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF
… object door Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a8e485cacd722ffc9b31133cca6bd08d5b1bd4b5 && git checkout a8e485cacd722ffc9b31133cca6bd08d5b1bd4b5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4049efa34584a18485ccd13efcf4351f509205eb 365a6e4830fd27520c07daee7f1de542a2e77121 && git checkout -B drift-repro 4049efa34584a18485ccd13efcf4351f509205eb && git merge --no-ff 365a6e4830fd27520c07daee7f1de542a2e77121
node scripts/docs-audit/affected-docs.mjs --json 4049efa34584a18485ccd13efcf4351f509205eb
|
Contract reviewServed-tier: Card #26646 (body, triage ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentRead against the published spec on
Findings, none must-fix:
Check-runs on Implemented-by: VERDICT: PASS |
…e previous/parent in the docs Address the contract review's three should-fixes: the group slot's current_user/user/ctx/os prescription offers field-level security or a record rewrite instead of the select-option move, the header receiver table lists fieldGroups reads, and the docs paragraph names previous and parent beside record. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF
席位核对 · ACCEPT ·
|
Requested by Yibo Wang · project thread
Fixes #26646
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none —
skills/**never teachesfieldGroups[].visibleWhen(the two skills that mentionfieldGroupsteach key, label and membership only); the docs pagecontent/docs/ui/field-grouping-and-order.mdxgains the slot and its check here维护者速读
fieldGroups[].visibleWhen现在和字段自己的visibleWhen用同一套表达式检查,os validate/os build和对象保存口都会拦,错误规则expression-invalid,定位到分组的 key。record.no_such_field == 'x')以前照样 200 保存,渲染器按失败即隐藏处理,整个分组(连标题)在所有表单和详情页消失,没有任何提示;同样的写法放在字段或动作上早就被 422 拒绝。visibleWhen,零命中。回滚即撤销本 PR。packages/lint/src/validate-expressions.ts(对象遍历末尾新增的分组循环,以及fieldRuleRootIssue里为分组改写的措辞);packages/lint/src/runtime-gate.object-field-group-visibility-writes.test.ts;packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts最后一个 describe 块。pnpm --filter @objectstack/lint exec vitest run src/runtime-gate.object-field-group-visibility-writes.test.ts,预期 14 个用例全绿,其中卡片原样探针被拒、定位object 'qa_fg_probe' · field group 'g1' visibleWhen。What changes
@objectstack/lint— the field-group pass.runStackExpressionPassesgains one loop at the end of the object walk, after the field walk: everyfieldGroups[].visibleWhenis judged with the fieldvisibleWhenslot's rules — therecord-scopedcheck(it parses, readsrecord.FIELDand never a bare field, names a field the object declares, calls only registered functions) and the root verdict overrecord/previous/parent. Same rule id (expression-invalid), located atobject 'NAME' · field group 'KEY' visibleWhen. Not fenced on an object write, so the object save door (PUT /api/v1/meta/object/:name, draft promotion, package draft publish) gives the build's finding, andos validate/os buildgive it too.fieldRuleRootIssuetakes afield group visibleWhenslot: the message opens with "visibleWhenreadscurrent_user, but a field group's section predicate binds onlyrecord…", and its consequence cell names the group's fail-closed gate (the renderer'ssectionGateVisibleresolves a fault to hidden, where a field's ownvisibleWhenfalls back to visible). The field slots' wording is unchanged.authoring-rules.tsbeside the finding(lint): the object save door gives no build verdict on validation conditions, field-rule slots (requiredWhen etc.), option visibleWhen or action predicates; os build refuses them, a metadata save stores them (#22019's sibling) #22032 pass history, with the corpus measurement.content/docs/ui/field-grouping-and-order.mdxon the group'svisibleWhen, its fail-closed gate and the check..changeset/26646-field-group-visible-when-judged.md,@objectstack/lintminor, both declaration lines, ADR-0087not-required (no-migration-prescription).Why
The form renderer gates the whole group on this predicate and fails it closed, so a misspelt field hid the section on every form and record page with nothing said at authoring time, while the same text on
fields.*.visibleWhenoractions[].visiblewas refused 422 at the same door. No pass of the expression rule read the slot (validate-expressions.tshad nofieldGroupssite).Tests
packages/lint/src/runtime-gate.object-field-group-visibility-writes.test.ts(14 cases): the card's probe body and four more refused bodies (bare reference, unregistered function, syntax error,current_userroot) each refused once at the group's key; three valid predicates and an absent one publish clean at the door and the build; door findings equal build findings; the card body earns the same message on the group as on the field's ownvisibleWhen; a stored sibling's broken group does not block this write.packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts(4 cases) through the realsaveMetaItem/publishMetaItem: the card's probe is refused422 INVALID_METADATAon an active save and on a draft's promotion with the finding at the group's key and nothing landing;record.stage == 'won'saves; door issues equal build findings.validate-expressions.test.tsmeta-test:objreadsfieldGroups, and the newfieldGroupreceiver (key,visibleWhen) is checked againstObjectFieldGroupSchema.runtime-gate.object-formula-writes.test.ts: the "every object-borne site" pin carries a faulting groupvisibleWhen, at the build's position (after the option site, before the actions).Acceptance notes
031d97a0(fresh showcase boot, seed admin): the card's step 1 answered 200state=activeand read the predicate back verbatim; step 2 (actions[].visible) and step 3 (fields.stage.visibleWhen) answered 422expression-invalid. Premise holds.contact4 andsemantic_zoo2, thecreate-objectstackblank template 1, theos initscaffold 1; platform objects declare none) and none carries avisibleWhen. Nothing to fix.current_useron a group is refused, as on a field's ownvisibleWhen. In a form whose host binds the user it used to hide the group by role in the renderer only; elsewhere it faulted and hid the group for everyone. The prescription points at field-level security for role-based hiding.Verification
All at head
75bff196c(main merged) unless noted.Live replay on a showcase boot (head 6df7deb, lint dist rebuilt)
INVALID_METADATA, one issue, ruleexpression-invalid, pathobject 'qa_fg_probe' · field group 'g1' visibleWhen, message "unknown fieldno_such_fieldonqa_fg_probe".GET /meta/object/qa_fg_probeafterwards: 404.record.stage == 'won'on the same group: 200state=active.action 'qa_go' visibleand atfield 'stage' visibleWhen.os validate(app-showcase) withvisibleWhen: "record.no_such_field == 'x'"planted on theworkgroup: exit 1,object 'showcase_contact' · field group 'work' visibleWhen: unknown field; restored, exit 0 with 0 field-group findings. app-crm and app-todovalidate: exit 0.Suites
pnpm --filter @objectstack/lint test: 170 files, 7485 tests passed.typecheck: exit 0.@objectstack/metadata-protocolprotocol.runtime-authoring-gate.test.ts: 218/218.typecheck: exit 0.pnpm --filter @objectstack/dogfood test, closure built): 334 files passed, 3 skipped; 2716 tests passed, 20 skipped; lock VERDICT command-exit 0.Ablation (protocol block reads lint through dist)
Mutated the loop to read
obj.ABLATED_fieldGroups_26646withscripts/ablation-replace.mjs --hold, rebuilt lint,ablation-dist-preflightfound the marker in 4 built files: the #26646 protocol block went 3 failed / 1 passed (the two refusals and the parity case red, the valid control green). Restored (blob == HEAD,git diff HEADempty), rebuilt, preflight--absentclean: 4/4 green.Derived gates
node scripts/pm/dispatch-gates.mjs --commandsderived 88 families; all 88 ran with exit 0.--ranreconciliation: 88 derived, 88 run, 0 NOT-MEASURED, 0 UNRUN. CI runs the rest of the farm.Human Minimum
packages/specholds no runtime logic. (No spec change.)pnpm lintpasses. (CI owns the repo-wide lint run.)main, one pull request.packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzj32KwHUJobhFR5iHF