Repository navigation
feat(plugin-approvals): the request page's Details tab ends with the request snapshot's raw JSON, gated on studio.access - #23837
Conversation
…raw request snapshot as a record:json panel gated on studio.access The Details tab of the served sys_approval_request_detail page now holds record:details, then a record:json block on `payload` (the parsed snapshot the request read serves), gated on requiredPermissions ['studio.access']. The page pin holds the tab's children, and a new pin reads one request through ApprovalService.getRequest (the read the console's request page loads its record through) and asserts the key the panel names is served and the stored column payload_json is not. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018VqJPGA1NDNSCZsnecsChP
… judged by the props call The page schema's parse does not judge a node's props bag, so the pin that the panel's requiredPermissions holds is the props call's refusal of a panel without it. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018VqJPGA1NDNSCZsnecsChP
…er's bound and refuses operators it does not implement check:objectql-double-limit and check:where-matcher judge every new find double. The double now applies `limit` after the filter, by presence, and copies only the bounded rows. Its matcher lives at module scope, where the matcher gate can lift it, and throws on a combinator or operator outside equality, `$in`, `$or` and `$and`. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018VqJPGA1NDNSCZsnecsChP
…proval-request-raw-json
📓 Docs Drift CheckThis PR changes 1 package(s): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 6 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 67a62fb599e6a8997c63613edfcb827822d32b5a && git checkout 67a62fb599e6a8997c63613edfcb827822d32b5a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0cfd0c49bd2635da048fae96b35a38448141fa9d 54aa39a949185bbb90760ac3f128076f9d646e4d && git checkout -B drift-repro 0cfd0c49bd2635da048fae96b35a38448141fa9d && git merge --no-ff 54aa39a949185bbb90760ac3f128076f9d646e4d
node scripts/docs-audit/affected-docs.mjs --json 0cfd0c49bd2635da048fae96b35a38448141fa9d
|
席位核对 · content reviewed, landing HELD ·
|
Fixes #23746
Clause-②: no
The services half of face 4 of the ruling on objectstack-ai/objectui#12438 (letter C,
6190549995, item 4). The servedsys_approval_request_detailpage's Details tab now ends with the request snapshot's raw JSON.What changed
packages/plugins/plugin-approvals/src/sys-approval-request.page.ts: inslots.tabs.properties.items[0](Details),childrenis nowrecord:details, then{ type: 'record:json', properties: { field: 'payload', label: { en, zh-CN, ja-JP, es-ES }, requiredPermissions: ['studio.access'] } }. The labels are the inbox side sheet'sapprovalsInbox.rawDatacopy, authored inline like the page's other labels. The docblock gains one Strategy bullet: the panel is a platform operator's debug view gated onstudio.access, never the approver's read path.packages/plugins/plugin-approvals/src/sys-approval-request-page.test.ts: the pins (below)..changeset/23746-approval-request-raw-json.md:@objectstack/plugin-approvalspatch,Clause-②: no.sys-approval-request.object.ts,packages/spec/**or objectui.The three mechanism assumptions, measured
1.
record:jsonis declared and the literal is accepted.RecordJsonProps(packages/spec/src/ui/component.zod.ts:2606), registered at:8333and inpage.zod.ts:212. Measured on the real exported page (probe run at51d33dfe57; the pins then hold the result):PageSchema.safeParseObjectStackDefinitionSchema)validateComponentPropsrequiredPermissionscomponent-props-invalidat...children[1].properties.requiredPermissionsrequiredPermissions: []component-props-invalidat the same pathpathkeycomponent-props-unknown-keyat...properties.pathSo the page schema's parse does not judge a node's props bag. The props row does, through
os validate's props gate and the build rules. The page pin therefore holds the gate with a control on that call. A page-parse check alone would not cover it.2.
payloadis a key the page's record load serves. The read path the request page's record load takes, read end to end:63d00da, and the pindaeb77fc):apps/console/src/AppContent.tsx:305wraps the console adapter inrouteApprovalRecordReads. ItsfindOneofsys_approval_requestreadsGET /approvals/requests/:idthrough anApiDataSourceitem door (approvalRequestsDataSource.ts). That door'sfindOnereturns the response body unchanged (packages/core/src/adapters/ApiDataSource.ts:258).packages/rest/src/approvals-routes.ts:308-329answersres.json(row)withrow = svc.getRequest(id, context).ApprovalService.getRequestbuilds the row withrowFromRequest(approval-service.ts:1166). That function setspayload: parseJson(row.payload_json, undefined)(:1187) and copies nopayload_jsonkey. ThenredactPayloadsnarrowspayloadper reader (:1634).The new served-key pin drives that producer. It runs
ApprovalService.getRequestas the request's submitter over a stored row whosepayload_jsonholds a snapshot. It asserts that the key the page'srecord:jsonnames is served with the parsed snapshot, and thatpayload_jsonis not served. The generic data door (find/findOneon the engine) still answerspayload_json, redacted (payload-redaction-middleware.ts). The request page does not load its record through that door.3.
Clause-②: no. I built@objectstack/plugin-approvalsat the base5b6c257b06(a comparison worktree, closure from the shared turbo cache) and at51d33dfe57.dist/index.d.tsis byte-identical in both builds: sha2568d6525557b9d3cc681e0cf1e06c8dac6f163ebe535b4f36f99d1a87690d88b1e, andcmpexits 0. The page is not in the declaration closure:SysApprovalRequestDetailPagehas 0 hits in thed.tsagainst 1 for the controldeclare class ApprovalService. It does ship in the JS:record:jsonhas 1 hit indist/index.mjsat head and 0 at base.Pins
record:detailsthenrecord:json. The panel's non-copy props are exactly{ field: 'payload', requiredPermissions: ['studio.access'] }, and its label carries the plugin's four served locales. The label text is copy, so it is not pinned.requiredPermissionsremoved is refused byvalidateComponentProps(component-props-invalidat that path).PageSchema.safeParseof the exported page succeeds, and the component-type vocabulary, the props call and the build rules raise nothing on the page.Ablations
Both run at
e46780683bwithnode scripts/ablation-replace.mjs(wrap mode), anchored on the page file. The test imports the page by a relativesrcpath, so nodist/is involved.A. Drop the block (anchor hit 1, then 0; blob
42f3675ea82cto4be6cc90542c): 3 tests red, 15 green.B. Point
fieldatpayload_json(anchor hit 1, then 0; replacement 0, then 1; blob42f3675ea82cto08ffac93f084): 2 tests red, 16 green. The served-key pin is red because that key is never served:Restore after each leg, judged by the tool against the disk: blob after restore
42f3675ea82cequals the HEAD blob, andgit diff HEADis empty.Tests and gates
Everything below ran at head
54aa39a949unless a different sha is named. That head is this branch merged with origin/main43567a8fcb. The merge brought two commits, and neither touches plugin-approvals,packages/spec/src/uiorpackages/lint. After the merge I rebuilt the 61-package build closure and re-ran everything below.pnpm --filter @objectstack/plugin-approvals exec vitest run --maxWorkers=2exit 0:Test Files 78 passed (78),Tests 1084 passed (1084).e46780683b.pnpm --filter @objectstack/plugin-approvals typecheckexit 0. The test file is in thetsconfig.test.jsonprogram (--listFilescount 1) with 0 errors. The buildtsconfig.jsondoes not include tests.pnpm --filter @objectstack/spec check:generatedexit 0 after the merge. All 14 generated artifacts are up to date.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 65 commands. All 65 ran, and every exit code was captured before any pipe: 65 exit 0.--ranreconciliation: "65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN". The zero is derived from the recorded exit codes, and none of them is 3.check:objectql-double-limitandcheck:where-matchereach found a real problem in the new engine double. The double was blind tolimit, it then copied rows before applying the bound, and its matcher could not be lifted from inside an async function.e46780683bfixes all three, and both gates are green on the head.check:i18nandcheck:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (exit 3, nothing measured). After the CLI build closure those gates name, both exit 0.pnpm lint,test:repo, and the rest of the lint-workflow farm. eslint on the two touched TypeScript files exits 0.Acceptance notes
record:jsonrenderer yet. I found no registration at objectui origin/main63d00daor at the pindaeb77fc, and the type is not in objectui's placeholder list. Until objectui registers the renderer (approvals face 4 of 5 (ruling C on objectui#12438): the served sys_approval_request page carries the raw-payload panel as a generic block gated on studio.access declared in the page metadata, as the inbox side sheet gates it objectui#12490 step 4) and this repo's.objectui-shamoves to it,SchemaRendererdraws this node as its red OBJUI-001 "Unknown component type: record:json" panel (packages/react/src/SchemaRenderer.tsx:2151-2175). That panel prints the node's schema JSON, not the payload, and every viewer of a request's Details tab sees it, approvers included. I read this from the source and did not check it in a browser. The step order (page edit, then renderer) is the ruling's. When this lands relative to the pin move is the landing seat's call. This PR stays draft..changeset/23745-record-json-block.md(pending, unreleased) says: "objectui draws it and@objectstack/plugin-approvalsplaces it on the approval request page after this release". If this PR lands before that changeset is released, plugin-approvals places the block in the same release. The file is outside this card's surface and is left unchanged. The report to the dispatching seat names the one-line edit.fieldorrequiredPermissions"is refused at parse". Measured above, the refusal comes from the props gate atos validate/os build(component-props-invalid). The page and stack parses accept such a node.Generated by Claude Code