Repository navigation
fix(lint)!: refuse a stored page sort naming an unknown field, on every page sort slot (#30093) - #30270
fix(lint)!: refuse a stored page sort naming an unknown field, on every page sort slot (#30093)#30270objectstack-fleet[bot] wants to merge 3 commits into
Conversation
…ry page sort slot Every stored page sort slot (element dataSource.sort, a related list's sort and parentDataSource.sort, each sort-carrying block's own sort, interfaceConfig.sort, an app page's record.sort) is now asked existence and provenance by validateSortableFields, the member the page publish door runs: an unknown name is page-field-unknown at error, as the list door answers it 400 INVALID_SORT on every read. validatePageFieldBindings no longer reads a page sort, so each slot has one reader. Claude-Session: https://claude.ai/code/session_01Utp6CYvKbGaAgjBLQdkwPB Co-authored-by: Claude <noreply@anthropic.com>
📓 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 a820e9b5c395f0f57ce5d4fcdef29576b7912f6c && git checkout a820e9b5c395f0f57ce5d4fcdef29576b7912f6c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 04f36b63f8d2f32b00428929b07268130881e30f 647a1c1fda4e3b6426696b10e82370e612a558a4 && git checkout -B drift-repro 04f36b63f8d2f32b00428929b07268130881e30f && git merge --no-ff 647a1c1fda4e3b6426696b10e82370e612a558a4
node scripts/docs-audit/affected-docs.mjs --json 04f36b63f8d2f32b00428929b07268130881e30f
|
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC Contract reviewServed-tier: Face: narrowing ( ① Derived judgmentsDeclarations (PR body and changeset, both line-initial):
Accept-set changes, each judged:
② Semver level
③ Boundary flags
④ Design judgmentNarrowing judged against the published spec and runtime on Against the card's acceptance: readers added for element Tests: Must-fix
Should-fix (same delta commit, seat's call on surface)
Implemented-by: VERDICT: FAIL |
…ed at the page door validating-metadata.mdx section 5 no longer lists a related list's sort as an advisory page-binding check, and the two code notes on the page crossing now say it also answers existence and provenance. Claude-Session: https://claude.ai/code/session_01Utp6CYvKbGaAgjBLQdkwPB Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC Contract reviewServed-tier: Delta review by the same isolated reader, PR #30270 (card #30093), narrowing face. Previous record on ① Derived judgments
② Semver levelUnchanged: ③ Boundary flags
④ Design judgmentThe narrowing itself is unchanged from the earlier record and stands: no declared capability removed, the spec's existing No must-fix. No should-fix. Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC 席位核对ACCEPT · head
|
domain:server#3 · os-bill · session_01Utp6CYvKbGaAgjBLQdkwPB
Requested by Bill · project thread
Fixes #30093
Clause-②: no (narrowing) — an unknown sort field on a page slot moves from silent or warning to error:
os validate/os build/os lintand thepagepublish door refuse a page sort they accepted before.Metadata-protocol: narrowed
Skills-docs: updated here — content/docs/deployment/validating-metadata.mdx
维护者速读
dataSource.sort、关联列表的sort与parentDataSource.sort、各列表类区块自带的sort、列表页interfaceConfig.sort、应用页record.sort)写了对象没有的字段时,os validate和页面发布门一律报page-field-unknown错误,指到具体槽位路径,并给出最接近的字段名。400 INVALID_SORT。之前只有两个槽位在命令行给警告,其余槽位没人检查,页面照样发布成功,第一次打开就坏。created_at这类系统列、本栈没有的对象、没有字段表的对象都不判。回滚 = revert 本 PR。packages/lint/src/validate-sortable-fields.ts的checkPageSortDeclaration(三问的顺序与跳过条件);packages/lint/src/validate-page-field-bindings.ts的isPageSortRef(这里不再读页面排序,避免一处报两次);packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts末尾的 lint: page sorts naming an unknown field get no reader on element dataSource.sort / app page record.sort, and stay warning elsewhere while the door answers 400 #30093 用例。pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.runtime-authoring-gate.test.ts -t 30093,预期 6 个全绿:5 个槽位各自被 422INVALID_METADATA拒绝、page-field-unknown指到槽位路径、没有存下任何行;对照组用真实字段全部保存成功。What changes
validateSortableFieldsasks every stored page sort slot three questions, in the list door's order (checkPageSortDeclaration): existence (page-field-unknown,error), provenance (page-field-unprovisioned,warning, a registry-injected anchor on an ADR-0015externalobject) and sortability (sort-field-unsortable,error, unchanged from fix(lint)!: refuse a stored sort on asortable: falsefield, on list views and every stored page sort #30084). The slots and their bindings are the ones fix(lint)!: refuse a stored sort on asortable: falsefield, on list views and every stored page sort #30084 already walks, each bound to the object its renderer reads:dataSource.sort→dataSource.object; a block's ownsort→dataSource.object ?? BLOCK_OBJECT_PROP(PAGE_BLOCK_SORT_OBJECT_PROP);parentDataSource.sort→parentDataSource.object;interfaceConfig.sort→interfaceConfig.source ?? page.object;record.sort→record.object. Keys are read byreadSortKeys, so the legacy string form is judged by its head, and the-fieldshorthand and comma-separated keys are each judged.erroron every slot. The list door (assertSortFieldsExist) answers an unknown sort name400 INVALID_SORTon the first read and every read after it, which is the list view'ssort-field-unknowngrade. The id stays the page family's (page-field-unknown), so a page author reads the page rule.validateSortableFieldsis the one reference-integrity member that declarespageinruntimeTypes(fix(lint)!: refuse a stored sort on asortable: falsefield, on list views and every stored page sort #30084), so the page publish door (PUT /api/v1/meta/page/NAME; Studio, REST, MCP) now refuses the save422 INVALID_METADATAinstead of storing a page that breaks on its first read.validatePageFieldBindingsruns on the CLI only.validatePageFieldBindingsno longer reads a page sort (isPageSortRefsets aside a related list'ssortrefs;interfaceConfig.sortis no longer collected), so each slot has exactly one reader and one finding.relatedListFieldRefsandsortFieldRefskeep their exported behaviour for out-of-repo callers and the react surface.page-field-unknownfamily's own: a dotted relationship path (the door refuses it as its own verdict), a registry-injected system column (an author-declared field of a system name falls through to the projection, keeping fix(lint)!: refuse a stored sort on asortable: falsefield, on list views and every stored page sort #30084's verdict on it), an object the stack or the publish snapshot does not carry, and an object with no field map.Tests
validate-sortable-fields.test.ts, newdescribe(lint: page sorts naming an unknown field get no reader on element dataSource.sort / app page record.sort, and stay warning elsewhere while the door answers 400 #30093): every page slot refused aspage-field-unknownaterrorat its slot path (rule + path + severity asserted for all nine), message and fix line pinned once;validatePageFieldBindingsreports none of those slots while still reading a related list's columns (non-vacuity); each slot bound to its renderer's object; legacy string head,-fieldand comma keys; the skips; provenance on the external object atwarningand silence on the local twin; an author-declared system-named field keepssort-field-unsortable. The lint: a viewsortnaming a field the author declaredsortable: falseclears validate, then 400s on every load once #29896 lands —validate-sortable-fieldsjudges only the virtual predicate #30023 control no longer lists an unknown name as "not this rule's", and the one-line coverage pin now expects the two page ids beside the three sort ids.validate-page-field-bindings.test.ts: the related-list case drops its sort expectation; the lint: field-bearing props on React page blocks are unchecked against the bound object — close the class, not the next instance (#4329 follow-up) #4340 legacy-string case moves to the sortable suite and is replaced by a pin that this walk reads nointerfaceConfig.sortin either form while still reading the columns.protocol.runtime-authoring-gate.test.ts, new top-leveldescribe(lint: page sorts naming an unknown field get no reader on element dataSource.sort / app page record.sort, and stay warning elsewhere while the door answers 400 #30093): five slots (elementdataSource.sort,parentDataSource.sort, a related list'ssort,interfaceConfig.sort, an app page'srecord.sort) each refused422/INVALID_METADATAwith onepage-field-unknownissue aterrorat the slot path, nothing stored; control: the same pages on a declared field save.Acceptance notes
packages/lint/src/reference-integrity-suite.ts, thevalidateSortableFieldsmember note;packages/lint/src/authoring-rules.ts, thepagecrossing note) said it judges a page sort "by the sortability projection alone". Corrected in review round 1 (comments only).kind: 'react'page's sort props stay withvalidate-react-page-props(sortFieldRefs, theskippedgrade). They are JSX props, not stored page sort slots, and are outside this card.Review round 1: what changed
Head
df135352→647a1c1f, one commit, after the contract review (FAIL, comment 6649790600): one must-fix and one should-fix, both taken.content/docs/deployment/validating-metadata.mdx§5 said arecord:related_list'scolumns/sort/filterresolve through the page-binding rule and are advisory. It now namescolumns/filteronly, and a new paragraph says a page sort is the exception: every stored page sort slot naming a field the bound object lacks is apage-field-unknownerror, and the page publish door refuses the save422 INVALID_METADATA, because the list door answers it400 INVALID_SORTon every load. TheSkills-docs:line in this body and the changeset now readsupdated here — content/docs/deployment/validating-metadata.mdx.reference-integrity-suite.tsnear line 289,authoring-rules.tsnear lines 1253–1256) now say the page crossing also answers existence (page-field-unknown,error) and provenance (page-field-unprovisioned,warning). Comments only.Round 1 verification, head
647a1c1f.node scripts/pm/dispatch-gates.mjs --commandsnow derives 87 families (the docs edit adds 29, the docs-audit and spec doc families among them); all 87 exit 0,--ran: 87 derived, 87 run, 0 NOT-MEASURED.pnpm lint: exit 0.@objectstack/lintrebuilt after the comment edits. The source change since round 0 is comments and one docs page, so the round-0 test, dogfood and reverse-verification readings below still describe the code.Round 1 gate record (87 derived + pnpm lint)
Verification (round 0, head
df135352)All readings on head
df135352(this branch withorigin/mainat6f095b9cmerged), after a fullturbo run build --filter='!@objectstack/docs'under the verify lock (exit 0).pnpm --filter @objectstack/lint test: Test Files 193 passed (193), Tests 8001 passed (8001).pnpm --filter @objectstack/lint run typecheck: exit 0.pnpm --filter @objectstack/metadata-protocol test: Test Files 312 passed, 4 skipped (316); Tests 22360 passed, 26 skipped.pnpm --filter @objectstack/metadata-protocol typecheck: exit 0.pnpm lint: exit 0.node scripts/pm/dispatch-gates.mjs --ran: 58 derived families, 58 run, 0 NOT-MEASURED, every exit code 0.pnpm --filter @objectstack/dogfood test, the title carries!): Test Files 1 failed, 418 passed, 3 skipped (422); Tests 1 failed, 3487 passed, 36 skipped. The one failure istest/permission-set-clone-door.dogfood.test.ts› "a platform set … copies into its shipping package's pair" (422: permissionmy_member_baselinegrants onsys_comment, "not defined in objects"). It is not this diff:mainat6f095b9cis red on the same test (Dogfood Regression Gate (2/3), job 127024968149), and this diff touches no permission, object or clone path.origin/main(git restore --source=origin/main), the two lint suites go 8 failed / 122 passed: every new lint: page sorts naming an unknown field get no reader on element dataSource.sort / app page record.sort, and stay warning elsewhere while the door answers 400 #30093 case and the two moved page-field-bindings cases, plus the one-line coverage pin. With lint rebuilt from those sources, the door suite goes 5 failed / 1 passed: the five refusals red (the page saved), the control green. Both restored withgit checkout HEAD --,git statusclean.Gate record (58 derived + pnpm lint)
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01Utp6CYvKbGaAgjBLQdkwPB