Repository navigation
fix(plugin-detail): draw the related-list action-refusal notice for authors, not end users (objectui#11768) - #11770
Merged
objectstack-fleet[bot] merged 3 commits intoOct 7, 2026
Conversation
…uthors, not end users (objectui#11768) The notice naming an authored action id the related object does not define was drawn for every viewer. It is now drawn only for a viewer holding the metadata-edit capability (manage_metadata, read through hasCapabilities as useCanAuthorMetadata reads it, fail-open on an unreported set) or in dev mode (the build's NODE_ENV). The refused entry stays undrawn for everyone and the resolved entries render for everyone. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…ce audience (objectui#11768) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…-byte (objectui#11768) The earlier edit dropped the space after `=` on a line the change never meant to touch; this puts it back exactly as on main. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-11768-related-list-refusal-audience
branch
October 7, 2026 14:31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11768
Clause-②: no
What changes
record:related_listnames an authored action id it cannot draw in arole="status"notice above the list (data-testid="record-related-list-actions-refused", added by objectui#11163). The notice had no viewer condition, so an end user opening a record read an authoring fault about ids they can neither fix nor act on.os validatealready refuses such an id at build time (objectstack-ai/objectstack#20936).The renderer now draws the notice only when:
manage_metadata, orEverything else stays as it was, for every viewer:
refusedActionsis non-empty.os validate's refusal is untouched (this PR changes no objectstack file).Files:
packages/plugin-detail/src/renderers/record-related-list.tsx, the new pin filepackages/plugin-detail/src/__tests__/RecordRelatedListRenderer.refusalAudience-11768.test.tsx, and.changeset/11768-related-list-refusal-audience.md(patchon@object-ui/plugin-detail). These are all inside the claim's file surface.The two readings this PR takes (named, per the dispatch)
1. Who can change the page:
manage_metadata, read fail-OPEN. This is the capability@object-ui/app-shellexports asAUTHORING_CAPABILITYand reads throughuseCanAuthorMetadata(). Studio's affordances consult that hook: the App to Studio bridge inAppHeader, the page-editor entry inPageView, and HomePage's builder CTAs. The renderer reads it the same way, throughusePermissions().hasCapabilities:systemPermissionscounts as holding it (fail open, objectui#4656). That covers a backend predating ADR-0066, the role-based provider, and no provider at all, as in the Studio designer;manage_metadata.The fail-open edge keeps today's behaviour for a provider that reports nothing.
METADATA_AUTHORING_CAPABILITY, not exported).plugin-detailcannot import app-shell, because app-shell depends on it. No packageplugin-detaildepends on exports the name.@object-ui/data-objectstackhasVIEW_CONFIG_CAPABILITYwith the same value, butplugin-detaildoes not depend on that package.AppHeaderandPageViewalso AND the capability withuseWorkspaceAdminStatus()(@object-ui/auth).plugin-detaildoes not depend on@object-ui/auth, and adding the dependency would move a manifest field outside the claim's surface. The capability is the server's own answer to "may author metadata"; the role is not (objectui#10899). If the seat wants the role conjunct too, that is a separate decision.2. Dev mode: the build's
NODE_ENVis notproduction. objectui has no named dev-mode flag. The seat's grep found none, and a re-read confirmed it. What the tree calls "dev-mode" throughout is the authoring-diagnostic guardglobalThis.process?.env?.NODE_ENV !== 'production'(plugin-gantt's andplugin-map'sisDev, and@object-ui/core'sactionKeys/reference-keys/column-identity). The renderer uses that same spelling, read per call. Measured with the console's own Vite 8.2.2 and itsdefineblock, on a probe holding this exact TypeScript spelling:vite build(production): the expression folds to the literalfalsein the emittedisDevBuild. The console'swindow.processshim inindex.html, which setsNODE_ENV: 'development'unconditionally, therefore never reaches it in a production bundle.transformRequest): the expression is left as written, and at runtime that shim answersdevelopment, so it istrue.NODE_ENVistest, so it istrue. The pins stub it explicitly.Not taken: the console's runtime debug mode (
?__debug,parseDebugFlags/useDebugMode). It is reachable from this package without new surface, but it is "debug mode", not "dev mode", and any viewer can switch it on from the URL. Also not taken:import.meta.env.DEV. A library-mode build replaces it statically, so the publisheddistwould always sayfalse.Pins (new file, real
SchemaRenderer+ real stockMePermissionsProvider, no mockedusePermissions)Production build (
NODE_ENVstubbed toproduction):manage_metadatasees no notice, the resolvedinvitestill renders, and the lookup did answer.manage_metadatasees the notice. It isrole="status", namesno_such_actionand says it is not an action ofcontact. The refused entry is still undrawn.Dev mode (
NODE_ENVstubbed todevelopment):manage_metadatasees the notice naming the id and the object.Ablation (one-shot, run through
ablation-replace.mjson the committed changef3daea7, restore proven each time)truegit diff HEADemptygit diff HEADemptygit diff HEADemptyAll three went in the expected direction (red). The test imports the renderer through
../index, a relative source path, so nodist/sits between the mutation and the run.Local verification (run on head
627d534)pnpm exec turbo run build --filter='@object-ui/plugin-detail^...' --concurrency=2: 11/11 tasks, exit 0.pnpm --filter @object-ui/plugin-detail type-check: the script name echoed,tsc --noEmit && tsc -p tsconfig.test.json, exit 0.--listFilesshows the test project compiles the new pin file.pnpm exec vitest run packages/plugin-detail/: 246 files passed, 1 skipped; 2414 tests passed, 8 skipped; exit 0.pnpm exec vitest run packages/app-shell/src/views/__tests__/RelatedRecordActionsBridge.recordRelated-11270.test.tsx(the other suite that reads the notice): 6/6 passed. It runs under vitest'sNODE_ENV=test, so the notice is drawn there exactly as before.pnpm exec eslinton the two touched source/test files: exit 0, 0 errors. The 15 warnings are all pre-existing; the same 15 show on the base file read through--stdin.check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations,check:vi-mock-specifiers,check:vi-mock-inherit,check:phantom-deps,check:unreferenced-sources,check-changeset-presence,check-changeset-no-major: all exit 0.exportline. Afterpnpm --filter @object-ui/plugin-detail build, nodist/**/*.d.tsnamesMETADATA_AUTHORING_CAPABILITYorisDevBuild, against a positive control:RecordRelatedListRendererPropsis indist/renderers/record-related-list.d.ts. No export, prop, type member or language-pack key is added.Acceptance notes
os validatemessage for this case ends "only a refusal notice naming it above the list". After this PR that is true for the reader the validator addresses: an author holdingmanage_metadata, or anyone in dev mode. The grade rules that refusal unchanged, so the wording is left alone; noted, not filed.packages/plugin-detail/README.mddocuments neither theactionskey ofrecord:related_listnor its refusal notice. Both have been undocumented since objectui#11163. It is a docs gap outside this claim's file surface; noted, not filed (carrier: none).Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8(dev subagent of thedomain:ui#3seat, claim comment 6038851078).Generated by Claude Code