Repository navigation
feat(spec): a generic record:json record block — one record key as indented JSON behind a required, fail-closed requiredPermissions gate - #23783
Conversation
…l-closed requiredPermissions gate (WIP) Claude-Session: https://claude.ai/code/session_01VG6wwFJVpmLcvLnZip8Srg Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VG6wwFJVpmLcvLnZip8Srg Co-authored-by: Claude <noreply@anthropic.com>
…ps and page-field rules Claude-Session: https://claude.ai/code/session_01VG6wwFJVpmLcvLnZip8Srg Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VG6wwFJVpmLcvLnZip8Srg Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 23 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 142 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 c0994c7fc08cb0604018c58c8959d56a1f22fc3b && git checkout c0994c7fc08cb0604018c58c8959d56a1f22fc3b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e5136ff10f1a0196d3ff4d971338a59506652297 2b82036d022893d9212c541ae4d6337fcf75cb6b && git checkout -B drift-repro e5136ff10f1a0196d3ff4d971338a59506652297 && git merge --no-ff 2b82036d022893d9212c541ae4d6337fcf75cb6b
node scripts/docs-audit/affected-docs.mjs --json e5136ff10f1a0196d3ff4d971338a59506652297
|
席位核对 · ACCEPT · PR #23783 @
|
Contract reviewServed-tier: Inputs read: card #23745 (body and all three comments: triage ① Derived judgmentsThe PR widens the accept set in two places and the public surface in one. Each change the two hunks imply, judged:
Nothing in the two hunks is wrong. Check-runs on the head at render time: 34 success, 5 skipped by the workflow's own path and event filters ( ② Semver level
③ Boundary flagsDev deviations (five, from report
Open questions (two):
Other boundaries read in the inputs:
Implemented-by: VERDICT: PASS Generated by Claude Code |
…origin/main Claude-Session: https://claude.ai/code/session_01VG6wwFJVpmLcvLnZip8Srg Co-authored-by: Claude <noreply@anthropic.com>
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38841078457 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #23745
Clause-②: yes
Regen-provenance: merge of origin/main at e5136ff plus check:generated regeneration; no source change since the reviewed head 33d9894.
What
The spec half of face 4 of objectstack-ai/objectui#12438's ruling (letter C, ruling
6190549995): a genericrecord:jsonrecord block. It draws one key of the bound record as indented JSON, in a panel that starts collapsed, with a copy control. A REQUIRED, fail-closedrequiredPermissionsgate sits in front of it.packages/spec/src/ui/page.zod.ts:'record:json'is aPageComponentTypemember. It is also classified inPRINT_REFUSED_PAGE_COMPONENT_TYPES(a panel that starts collapsed, behind a per-viewer gate).page-print.test.tsrequires every vocabulary member to be classified the day it lands.packages/spec/src/ui/component.zod.ts:RecordJsonPropsis strict, usesPROPS_HISTORYandCOMPONENT_LEVEL_GUIDANCE, and is registered inComponentPropsMapas'record:json'. It has three keys:field: required. A string in the snake_case machine-name grammar.label: optional.I18nLabelSchema, the summary line.requiredPermissions: required.z.array(z.string().min(1)).min(1). Its describe says the gate fails CLOSED, that it is presentation only and authorises nothing, and that the read route still serves the value.key/path→field,title→label,permissions→requiredPermissions).requiredPermissions(RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION) is untouched, as the ruling requires.record:jsondeclares the same key name as a separate declaration with its own text.check:generated --fix, five proved stale):api-surface/,authorable-surface/,declaration-map/,export-origins/,json-schema.manifest/,content/docs/references/**, and the strictness-ledger counts.@objectstack/specminor.Tables that enumerate record component types (each hit judged)
Hits of
git grep -n "'record:details'" -- packages/spec/src packages/lint/srcin source files:page.zod.tsPageComponentTypepage.zod.tsPRINTABLE_PAGE_COMPONENT_TYPES/PRINT_REFUSED_PAGE_COMPONENT_TYPESpage-print.test.ts)component.zod.tsComponentPropsMapreact-blocks.tsRECORD_CONTEXT_BLOCK_TAGSComponentPropsMapby therecord:prefix, so<RecordJson>on a react page is covered automatically (check:react-blocksgreen)react-blocks.tsREACT_RECORD_BLOCK_ALTERNATIVESlintvalidate-page-field-bindings.tsCOMPONENT_FIELD_SPECSfield's first value (payloadonsys_approval_request) is a route-served key, not a declared field, so an entry would refuse the downstream pageconversions/registry.ts(6 hits)record:detailsconversionpage.zod.ts:1097Test census edited:
component-record-block-field-security.test.tscounts the rows that declarerequiredPermissions. It now namesrecord:json. The "one key, one text" pin over the four shared declarations is unchanged.Liveness
packages/spec/liveness/page.jsonclassifies no component-level key.regionsis an undrilled container (#4956). Theregionsrow carries the component-member READ POINTS only as an evidence list with no verdict.record:jsonhas no renderer (objectui#12490 step 4) and no producer (#23746) yet. So there is no read point to cite and no row to add. Inventing one would be fabricated evidence. No gate requires a per-row entry: I greppedscripts/,packages/spec/scriptsandpackages/lint/srcfor a reader of that list and found none.check:livenessis green.Boundary:
fieldis not checked against declared fieldsI looked for an existing declared spelling that could say "a served key". The closest is
ObjectSchema.attachedOnRead(#22211, ruling A). It declares per-caller blocks with a FIXED set of scalar leaves (number | text | boolean | date). A request snapshot's leaves are the source record's fields, which vary per request. So it cannot describepayloadwithout being widened, and this PR does not widen it. Result: a misspelledfielddraws nothing, and publish cannot catch it. This is stated in the describe, the schema header and the changeset. The trade-off is recorded in the report'sopen_questionsfor the contract review.Acceptance notes
requiredPermissionselements arez.string().min(1), one notch tighter than the fit check'sz.string(): a gate naming''can never be passed and would draw nothing for everyone, silently.page.zod.ts. Whichever PR lands second mergesmain.record:jsonpanel of the request payload, gated onstudio.access(objectui#12490, face 4 of objectui#12438 letter C) #23746 (placing the block onsys-approval-request.page.ts) and objectui#12490 step 4 (the renderer) are not part of this PR.Tests
packages/spec/src/ui/component-record-json.test.ts(enum member, row identity, accepted contract, unknown key refusal with a lit control, near-spelling guidance,fieldgrammar, required and fail-closed gate, shared gate unchanged, print refusal).packages/lint/src/validate-record-json-23745.test.ts(component-type-unknownoffersrecord:json;component-props-unknown-key;component-props-invalidfor a missing or empty gate and a missingfield;page-field-unknownstays silent on a route-servedfield, with a firing control onrecord:highlights).type-alias-convention.pin.test.ts:RecordJsonPropspinned isomorphic (ADR-0122; 787 → 788).record:jsonrecord block — a bound record's value as indented JSON behind a REQUIRED, fail-closedrequiredPermissionsgate (objectui#12490, face 4 of objectui#12438 letter C) #23745 for commands, exit codes and the gate reconciliation.Generated by Claude Code