Skip to content

chore(sweep): retire ObjectLevelPermission and delete the console metadata duplicates (#4364, #4368) - #4410

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4364-4368-dead-surface-pair
Aug 12, 2026
Merged

chore(sweep): retire ObjectLevelPermission and delete the console metadata duplicates (#4364, #4368)#4410
yinlianghui merged 1 commit into
mainfrom
claude/issue-4364-4368-dead-surface-pair

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Part of #4364
Fixes #4368

Dead-surface deletions, post-retirement pair. Both cards record surfaces that lost their
last consumer when an earlier retirement landed, and both premises were re-measured at
this branch point
(456aac831) rather than taken from the cards. One of the two held
in full; one held in half, and the half that did not is reported rather than forced.

# Member Resolution Premise at 456aac831
1a #4364 ObjectLevelPermission retired zero consumers — its own definition plus the two barrel lines, nothing else
1b #4364 PermissionCondition kept — premise disproven types the parameter of evaluateCondition, under a 26-case suite
2 #4368 console duplicates deleted (2 files, 410 lines) zero importers; both had drifted behind the live app-shell copies

git diff --stat is 6 files: 2 deletions, 3 edited, 1 changeset. Nothing else.


Member 1a — #4364: ObjectLevelPermission retired

Ruling (PM seat, recorded on #4364, quoted verbatim):

#4364 — RETIRE both types (ObjectLevelPermission, PermissionCondition; PermissionEffect stays, it has the live FieldLevelPermission.effect reader). The enforce-or-remove default the maintainer applied to #3368 governs: zero-caller public export, no usage plan for role-attached grants, and the card's own warning — a named-but-unhonored type invites the next author to build against a contract the runtime removed — is the #4288 lesson one hop out. If role-direct grants gain business pull later, the types return WITH their reader.

Zero-consumer evidencegit grep -n 'ObjectLevelPermission' over the whole tree at
456aac831, excluding node_modules, four hits:

packages/permissions/src/index.ts:35:  ObjectLevelPermission,        [barrel re-export]
packages/types/src/index.ts:551:  ObjectLevelPermission,            [public surface]
packages/types/src/permissions.ts:42: * used to declare a second one (`permissions: ObjectLevelPermission[]`,   [#4288's own retirement note]
packages/types/src/permissions.ts:61:export interface ObjectLevelPermission {                                  [the definition]

Nothing constructs one, nothing accepts one, nothing reads one — the only non-definition,
non-barrel hit is the prose in RoleDefinition's #4288 note. The grants the evaluator
actually applies come from ObjectPermissionConfig.roles, whose inner shape is declared
inline and never names this type.

Deleted: the interface, and its line from both barrels
(packages/types/src/index.ts, packages/permissions/src/index.ts).

Retirement record — following the RoleDefinition convention PR #4366 established (a
doc-comment note at the survivor site naming the retirement and its issue), the note goes
on ObjectPermissionConfig, which is the survivor: it is the single wired home for
object-scoped grants. RoleDefinition's existing #4288 note gains one sentence recording
that the element type of its retired field has now gone the same way.

PermissionEffect stays, as the ruling requires — verified before touching the file
and after: FieldLevelPermission.effect at permissions.ts:71 still reads it. It lost
one referent (ObjectLevelPermission.effect) and keeps the live one.

Member 1b — #4364: PermissionCondition kept, premise disproven

The card states its case for this type transitively:

PermissionCondition is one step further out: its only referent is ObjectLevelPermission.conditions, so it inherits the same status transitively.

That is not true at this branch point, and it is not a drift — the referent predates the
card.
git grep -n 'PermissionCondition':

packages/permissions/src/evaluator.ts:14:  PermissionCondition,      [import]
packages/permissions/src/evaluator.ts:116:  condition: PermissionCondition,   [parameter type]
packages/permissions/src/index.ts:38:  PermissionCondition,
packages/types/src/index.ts:554:  PermissionCondition,
packages/types/src/permissions.ts:69:  conditions?: PermissionCondition[];   [the transitive referent the card names]
packages/types/src/permissions.ts:97:export interface PermissionCondition {

evaluator.ts:115 is an exported function that takes this shape and implements all
eleven of the operators the type declares, including a prototype-pollution guard:

export function evaluateCondition(
  condition: PermissionCondition,
  record: Record< string, unknown >,
): boolean {
  if (['__proto__', 'constructor', 'prototype'].includes(condition.field)) return false;
  ...
}

It is covered by 26 assertions in packages/permissions/src/__tests__/evaluator.test.ts
(lines 171-236) — one per operator, plus the three prototype-pollution cases. Deleting
ObjectLevelPermission removes the conditions? field at permissions.ts:69; it does
not leave PermissionCondition unreferenced, because evaluateCondition's referent
is independent of it. So this half of the ruling was declined rather than executed, and
the type's doc comment now records why it survived a sweep aimed at it.

The follow-on question this raises is real but is not ruled here, and is not mine to
guess: evaluateCondition is itself exported from evaluator.ts but not from the
@object-ui/permissions barrel (index.ts re-exports only evaluatePermission from
that module), and its only importer in the repo is its own test file. That is a
dead-surface shape one hop further out again — but retiring a function with real
behaviour, a security guard and a 26-case suite is a third judgement call that neither
the card nor the ruling names. Raised for triage on #4364 rather than acted on, which is
why this PR says Part of #4364 and not Fixes.

Member 2 — #4368: the two console-local duplicates deleted

Ruling (same claim comment, quoted verbatim):

#4368 — DELETE the console copies (apps/console/src/utils/metadataConverters.ts, apps/console/src/services/MetadataService.ts), not re-export: zero importers post-#4365 makes a re-export a dead surface with extra steps, and the app-shell copies are the live, tested ones. First step per the card: diff the copies for behavioral divergence — a divergence is EVIDENCE to record in the PR body (which behavior the live copy has), not a blocker. The dangling @object-ui/plugin-designer dependency + its lone vi.mock mention: measure and clean if the measurement confirms it dangles.

Zero-importer evidence, the card's own re-check command, run at 456aac831 (post-#4365):

$ grep -rn 'metadataConverters\|services/MetadataService' --include='*.ts' --include='*.tsx' apps/console/src
apps/console/src/services/MetadataService.ts:16: * @module services/MetadataService
apps/console/src/utils/metadataConverters.ts:10: * @module utils/metadataConverters

Two hits, both self-references in the deleted files' own docblocks. Zero importers, exactly
as the card predicted once PR #4365 removed objectDetailWidgets.tsx and
ObjectFieldDesignerWidget.tsx.

Copy divergence — measured first, and both copies had drifted

The card asks for the diff before the deletion, because which behaviour is the live one is
the fact that matters. Neither pair is byte-identical, and in both cases the live
app-shell copy is the one carrying the newer behaviour — the console copies are stale, not
merely redundant.

metadataConverters.ts (console 130 lines, app-shell 132) — the live copy reads one
more relational key:

   /** ObjectStack-convention key for the relational target (what the server serves). */
   reference?: string;
...
-    referenceTo: field.reference_to || field.referenceTo || undefined,          # console
+    referenceTo: field.reference_to || field.reference || field.referenceTo || undefined,   # app-shell

The live copy resolves referenceTo from field.reference, which its comment identifies
as the key the server actually serves. The console copy would have returned undefined
for exactly that shape.

MetadataService.ts (console 280 lines, app-shell 313) — the live copy carries the
#4373 view cache-invalidation seam; the console copy predates it entirely:

-import type { ObjectStackAdapter } from '../dataSource';                                 # console
+import { viewItemObjectName, type ObjectStackAdapter } from '@object-ui/data-objectstack';  # app-shell

   async saveMetadataItem(category, name, data) {
     ...
+    if (category === 'view') {
+      const objectName = viewItemObjectName(data);
+      if (objectName) this.adapter.invalidateViewKeys(objectName, name);
+    }

A view write through the console copy would have left the object page's override map
stale for the cache's 5-minute TTL — #4373's defect, on a writer nothing reaches. The live
copy also carries the doc block explaining why deleteMetadataItem is deliberately not
wired to that seam.

Both divergences are evidence for the deletion rather than against it: this is precisely
the imitation trap the card named — an author grepping for "the converter" or "the metadata
service" lands on a copy that no test covers and no user reaches, and drifts it further.
The app-shell copies and their suites (MetadataService.saveAdvisories.test.ts,
MetadataService.viewInvalidation.test.ts) are untouched.

The @object-ui/plugin-designer dependency — measured, and it does not dangle

The card's third item reads:

A third, smaller item in the same shadow: apps/console keeps its @object-ui/plugin-designer dependency, whose only remaining mention in the app is a vi.mock() in src/__tests__/AppContent.systemHubRoutes.test.tsx after the FieldDesigner consumer is deleted.

The textual observation is right and the conclusion does not follow. The dependency is
kept, and the vi.mock with it, because the measurement says the console's runtime
import graph still reaches the package — through its shell, not through its own sources.

apps/console/src/AppContent.tsx is a thin wrapper that renders DefaultAppContent from
@object-ui/app-shell. That module is packages/app-shell/src/console/AppContent.tsx,
and it lazy-loads plugin-designer for three route elements that are live in the console:

packages/app-shell/src/console/AppContent.tsx:68:  const CreateAppPage = lazy(() => import('@object-ui/plugin-designer')...)
packages/app-shell/src/console/AppContent.tsx:69:  const EditAppPage = lazy(() => import('@object-ui/plugin-designer')...)
packages/app-shell/src/console/AppContent.tsx:70:  const DashboardDesignPage = lazy(() => import('@object-ui/plugin-designer')...)
:637   Route path="create-app"                        element CreateAppPage
:800   Route path="design/dashboard/:dashboardName"   element DashboardDesignPage
:802   Route path="create-app"                        element CreateAppPage
:803   Route path="edit-app/:editAppName"             element EditAppPage

So the vi.mock('@object-ui/plugin-designer', …) in the console's own suite is
load-bearing, not a leftover of the deleted FieldDesigner: it stubs a module the
rendered tree lazily reaches, which is the flaky-import hazard AGENTS.md warns about.
The vite alias at apps/console/vite.config.ts:152 exists for the same reason — it is
what makes that lazy import resolve to source in the console dev server. Removing the
declaration would have removed a package the console genuinely serves at runtime.

node scripts/check-phantom-dependencies.mjs (the gate PR #4407 landed) is green on this
branch, and confirms the direction it checks: every import is declared by the package that
publishes it.


Residual sweeps after deletion

Each retired symbol swept individually across the whole tree, not only .ts/.tsx:

symbol / path residual hits verdict
ObjectLevelPermission 5, all in packages/types/src/permissions.ts doc comments this paper trail only — zero code referents
utils/metadataConverters 1, packages/app-shell/src/utils/metadataConverters.ts docblock the live copy
services/MetadataService 2, app-shell's own module + useMetadataService import the live copy
metadataConverters|MetadataService under apps/console 0 clean
PermissionCondition 6 — evaluator import + parameter, both barrels, definition, and the conditions? field's removal still fully wired, as intended

Verification

node scripts/check-phantom-dependencies.mjs   OK — 40 packages, 2647 files, 12701 specifiers
node scripts/check-control-bytes.mjs          OK — 4120 tracked text files
node scripts/check-changeset-presence.mjs     OK — 5 source files of 3 released packages, 1 changeset
node scripts/check-changeset-no-major.mjs     OK — no changeset declares `major`
node scripts/check-changeset-fixed.mjs        OK
eslint (the three edited files)               exit 0

pnpm --filter '@object-ui/types^...' --filter '@object-ui/permissions^...' \
     --filter '@object-ui/console^...' build  exit 0  (build closure first, fresh worktree)

pnpm exec turbo run type-check --concurrency=2
                                              Tasks: 78 successful, 78 total  (8m0.91s)

pnpm exec vitest run packages/types/ packages/permissions/ apps/console/ --maxWorkers=2
                                              Test Files  79 passed (79)
                                              Tests      967 passed (967)

CI on this branch has converged green as well — all 20 checks completed with no failure,
including Type Check, Lint (where this repo's family gates run), and all four
Test shards.

The repo-root vitest form is the one AGENTS.md mandates; the heavy phases ran under the
shared verification lock. The type gate is run repo-wide rather than scoped on
purpose: two packages' export surfaces narrow here, so the no-downstream-red proof is
load-bearing and a scoped run could not carry it — 78 of 78 packages type-check clean with
ObjectLevelPermission gone from both barrels.

Reverse verification is the retirement pattern's form — zero-consumer evidence plus a
green ladder after deletion (per #4365) — with the direction predicted before running
it: re-adding ObjectLevelPermission to a barrel while the interface stays deleted must
turn the type gate red, because a barrel cannot re-export a name that does not exist.

step expected observed
baseline tsc --noEmit in packages/types green exit 0
barrel line ObjectLevelPermission, restored to src/index.ts red src/index.ts(551,3): error TS2724: '"./permissions"' has no exported member named 'ObjectLevelPermission'. Did you mean 'RowLevelPermission'?
restored via git checkout -- packages/types/src/index.ts green exit 0, tree clean

One honest correction to the prediction: the expected code was TS2305 (the plain
no-exported-member error); the compiler emitted TS2724, its "did you mean" variant,
because RowLevelPermission is a near-miss neighbour in the same export block. Same
failure, more specific code. The fix was taken out with git checkout, never git stash
that stack is shared across every worktree of the repo.

No pin tests are added. For member 1a there is nothing left to pin — the type is gone from
both barrels, and a re-introduction is a type error rather than a silent regression. For
member 2 the deleted files had no test file at all (that absence is the card's point), and
the behaviour they duplicated is already pinned on the live app-shell copies.

Changeset

.changeset/dead-surface-pair-4364-4368.md@object-ui/types minor,
@object-ui/permissions minor, @object-ui/console patch. Both barrels narrow, so
both released packages are graded as export narrowings; minor rather than major per
AGENTS.md, which reserves objectui's major for tracking @objectstack and requires
objectui's own breaking changes to be minor with the breaking semantics spelled out in
the body. @object-ui/console takes patch for the two file deletions, following the
#4365 precedent for console-only deletions. The changeset body also records the
PermissionCondition and PermissionEffect retentions so a reader of the release notes
does not infer the whole permission-condition surface went.

Note for triage

This PR opens with Part of #4364, not Fixes — the ruling names two types and one of
them is measured live, so merging this does not close that card. #4368 is delivered in
full and carries Fixes. The open question for #4364 is whether evaluateCondition
(zero production callers, not barrel-exported, 26-case suite) should itself be retired,
which is what would free PermissionCondition; that is recorded on the card rather than
filed as a separate issue, since it sits inside #4364's own completion scope.


Generated by Claude Code

…adata duplicates (#4364, #4368)

Post-retirement dead-surface pair, both premises re-measured at 456aac8.

#4364 — `ObjectLevelPermission` is retired from `@object-ui/types` and the
`@object-ui/permissions` re-export. Its only referents were its own definition
and the two barrel lines; the wired home for object-scoped grants is
`ObjectPermissionConfig.roles`, which declares its grant shape inline. The
retirement note goes on that survivor, per the RoleDefinition convention from
PR #4366.

`PermissionCondition` is KEPT — the card's premise ("only referent is
ObjectLevelPermission.conditions") does not hold: it types the parameter of
`evaluateCondition` in packages/permissions/src/evaluator.ts, under a 26-case
suite. Its doc comment now records why it survived a sweep aimed at it.
`PermissionEffect` is untouched; FieldLevelPermission.effect still reads it.

#4368 — the two console-local duplicates are deleted (410 lines, zero importers
post-#4365). Both had drifted behind the live app-shell copies they duplicate:
the console converter never read the server's `reference` key, and the console
service predates the #4373 view cache-invalidation seam. The
`@object-ui/plugin-designer` dependency is KEPT — it does not dangle; app-shell's
DefaultAppContent, which the console renders, lazy-loads it for four live routes,
so the console suite's vi.mock is load-bearing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 12, 2026 4:03am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-hQCpaBew.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.20KB 108.43KB
core (index.js) 2.99KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 153.42KB 41.19KB
fields (index.js) 228.33KB 56.61KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.33KB 1.20KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 62.18KB 17.67KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 120.57KB 31.32KB
plugin-designer (index.js) 211.06KB 42.70KB
plugin-detail (index.js) 239.03KB 59.77KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.14KB 39.98KB
plugin-grid (index.js) 187.99KB 49.92KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.18KB 26.76KB
plugin-map (index.js) 18.05KB 5.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.99KB 10.74KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.71KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

ACCEPT — PM 复核 (session session_017Qqyix2QcnpUC9XeYVDzx3), closes #4368; #4364's disposition ruled on its card.

  • The declined half is the review's centerpiece and it is CORRECT: my ruling inherited the card's transitive premise, and the premise was false at the branch point — PermissionCondition types the live, tested, security-guarded evaluateCondition. Stopping that member, switching to Part of #4364, and escalating with a three-axis recommendation instead of executing a wrong ruling is exactly what the stop condition exists for. The ruling is corrected on [finding] ObjectLevelPermission has no consumer left once RoleDefinition.permissions is retired — a published type nothing references #4364.
  • The copy-divergence diff turned evidence into vindication: both console copies were STALE — one missing the server-served reference key read, the other predating the app-shell: publishing a draft view does not invalidate the adapter's view caches — the console's real create-view flow bypasses ObjectStackAdapter entirely #4373 invalidation seam entirely (a view write through it would have staled the override map for the full TTL). That is the imitation trap measured, not asserted.
  • The dangling-dep KEEP is a measured save: the console's runtime graph genuinely reaches plugin-designer through app-shell's lazy routes, the vi.mock is load-bearing, and the reasoning is written down so no future card re-derives it textually.
  • Changeset grading (types/permissions minor with retentions recorded, console patch) matches both precedents; the TS2724-vs-TS2305 prediction correction reported rather than papered over; CI converged 20/20.

Flipping ready + arming auto-merge.


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 12, 2026 04:22
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 2a40f69 Aug 12, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4364-4368-dead-surface-pair branch August 12, 2026 04:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants