Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

fix(plugin-grid,types): an import undo that leaves rows unreverted says so (objectui#13015) - #13071

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-13015-undo-partial-failures
Oct 19, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-13015-undo-partial-failures

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #13015
Clause-②: yes (narrowing) (ImportJobUndoResult, exported from @object-ui/types, gains the required failures member. The new grid.import.undoPartial count family widens @object-ui/i18n's TranslationKeys, and failures is now required of a custom DataSource's undoImportJob answer.)

What this does

The undo route answers 200 with success: false and one failures entry per row a control refused. That shape landed in objectstack-ai/objectstack#24431, which objectui's pin f4755a68a797 carries. The job stays undoable for exactly those rows. The wizard never read the answer.

  • The hook reads the answer. useImportJobUndo (packages/plugin-grid/src/ImportWizard.tsx) now takes the result of ds.undoImportJob(jobId). On success: false it hands the answer to the list that ran Undo. A throw is handed over as before. The object's readers still get notifyDataChanged, because a partial undo reverted the other rows.
  • Both lists draw it. The History list and the cancelled-result screen render one shared ImportUndoOutcomeNotice. It shows:
    • how many rows were not undone (failed), and that undoing again retries only those rows (grid.import.undoPartial, a count family in all ten packs);
    • each refusal sentence the answer's failures[].error carries, the app's own words. Rows with the same sentence share one line, because one hook typically refuses many rows alike (an undoable job holds up to 5,000 rows). After ten distinct sentences the rest are counted with the existing grid.import.moreErrors.
  • Undo stays offered after a partial undo, because the re-read job is still undoable and not revertedAt. That is correct per the contract, so the button is not hidden. Undoing again clears the notice as it starts.
  • A full undo is unchanged. success: true shows nothing new and flips the row to "Undone" as before.
  • ImportJobUndoResult is bound to the spec. It is now UndoImportJobResponse from @objectstack/spec/api, by reference, the way ImportRowResult and its siblings at the top of packages/types/src/data.ts already are. The hand-written copy it replaces said it mirrored the server and lacked failures; that drift is this card's defect.

Bounded in-place fix in the same hook (named here, with evidence)

In the History list, a thrown undo also showed nothing. The hook wrote the message into the panel's error slot, then called the reload, and load() clears error as it starts, in the same tick. The partial notice would have been wiped the same way, so the undo outcome now has its own slot (undoOutcome), and the thrown message goes there too.

  • Evidence: a one-off probe against the base file (c2191654a): render the History list, an undo that rejects with a sentence, wait for the reload, and assert the sentence is on screen. Result: 1 failed, expect(received).toBeInTheDocument(). The probe was deleted, and the file was restored to HEAD (blob equal, git diff HEAD empty).
  • The four conditions hold. It is the same defect class (an undo's outcome the user never sees, in the same list). It is mechanical (one state slot the reload does not touch). The file is under this claim. It adds no new gate surface. The committed pin is History list: an undo that throws shows its message after the list reloads.

Premises measured (PM hypotheses)

  • H1 held. At c2191654a the hook ran await ds.undoImportJob(jobId); notifyDataChanged(...) and its only error path was the catch. It had two callers: the History list and the cancelled-result screen.
  • H2 held, so the producer side does not move. The server's route (objectstack f4755a68a797, packages/rest/src/data-action-routes.ts) answers a flat res.json({ success, jobId, object, deleted, restored, failed, failures }). The installed client at the pin (node_modules/.cache/objectstack/client, 18.0.0-next.5) has unwrapResponse unwrap only a body that has data, and its fetch throws only on !res.ok. @object-ui/data-objectstack's undoImportJob returns the client's answer unchanged. So failures already reached the wizard at runtime; only the type and the UI dropped it.
  • H3: derived, not hand-mirrored. check:spec-symbols does not require it: the local name differs from the spec's, and the comment named no @objectstack/spec symbol. But the tree's convention (the header block of packages/types/src/data.ts, "Spec-owned names are bound here, not re-declared") and commandment #0 point the same way, and the hand copy is exactly what drifted. Reading failures through the spec makes @object-ui/types the first consumer of the release that first publishes it, so the floor moves (below).
  • H4 held. After a partial undo, both lists re-read the job and it still offers Undo. The fix is the message, not hiding Undo.

Spec floor

@object-ui/types moves @objectstack/spec from ^18.0.0-next.2 to ^18.0.0-next.5.

  • R = 18.0.0-next.5, the first published release carrying UndoImportJobResponseSchema.failures (objectstack adb7910f8, PR fix(rest)!: an import undo runs under the object's declared controls for every caller but the system principal objectstack#24431). The registry lists 18.0.0-next.0 to 18.0.0-next.3 and 18.0.0-next.5; 18.0.0-next.4 was never published.
  • git merge-base --is-ancestor adb7910f8 against the @objectstack/spec@18.0.0-next.5 tag commit gives exit 0. Against the …next.3 tag commit it gives exit 1. The control leg (the …next.2 tag commit against …next.3) gives exit 0, so the exit 1 is a real non-ancestor on that checkout.
  • The packed tarballs agree: failures: z.ZodArray has 2 hits in next.5's dist/api/index.d.mts and 0 in next.2's.
  • The lockfile does not move: the importer resolves the spec through the pin override (file:../../.objectstack-pin/spec).
  • @object-ui/plugin-grid keeps its own floor. It reads failures only through @object-ui/types, and its artifact names no UndoImportJobResponse.
  • Scoped check:spec-floors over @object-ui/types: 0 findings at ^18.0.0-next.5. The run also clears the two floor-too-low findings @object-ui/types had at next.2 (checkPageMainRegionWidth, checkPageRecordBindingPlacement); next.5's dist/ui/index.d.mts carries checkPageMainRegionWidth (1 hit, 0 in next.2). That is objectui#13065's subject; this PR closes no card but this one. The whole-repo run exits 1 only on 14 no-artifact findings for packages outside this branch's build closure.

Tests

All runs below are at 13af833 (the source has not changed since 92728ce).

  • New file packages/plugin-grid/src/ImportWizard.undoPartial-13015.test.tsx, which drives the real wizard with a stubbed data source: 4 tests, 4 passed.
    • History list, partial answer (success: false, failed: 3, three failures with two distinct sentences). It shows "3 rows were not undone. Undo again to retry only those rows.", lists both sentences once each, survives the reload, keeps Undo offered, and emits one data change.
    • History list: a second Undo answering in full clears the notice and shows "Undone".
    • History list: a thrown undo shows its message after the reload.
    • Cancelled-result screen, the same partial answer: count, both sentences and retry sentence shown, and Undo still offered, because the re-read job is undoable.
  • pnpm exec vitest run packages/plugin-grid/: Test Files 229 passed (229), Tests 2077 passed (2077).
  • pnpm exec vitest run packages/i18n/ packages/types/ (the ten-pack key parity, plural-category and count-family pins, plus the types suite): Test Files 472 passed (472), Tests 11870 passed | 13 skipped (11883). The skips are the env-gated describe.runIf(DRIVEN) timezone suites, the only conditional markers in those two packages.

Ablations (one-off, through ablation-replace.mjs, each restored to HEAD with blob equality and an empty git diff HEAD)

The subject resolves to packages/plugin-grid/src (a relative import), and @object-ui/types resolves to src through the root vitest alias. No build was needed.

  1. Deleted the read, if (!answer.success) onOutcome({ kind: 'partial', answer });. Anchor 1 to 0. Result: 3 failed | 1 passed. The three partial-answer pins went red; the throw pin stayed green.
  2. Made the History reload clear the undo slot too (setLoading(true); setUndoOutcome(null); setError(null);), which is the base mechanism. Anchor 1 to 0. Result: 3 failed | 1 passed. The three History pins went red; the cancelled-screen pin stayed green. The first attempt at this leg was refused by the tool before any test ran, because the replacement contained the anchor. It is declared here and was not counted.

Reverse type check

A probe under packages/plugin-grid/src, type-checked through the rebuilt packages/types/dist, now refuses both old-shape literals. The old hand shape without failures gives TS2741 Property 'failures' is missing. An action: 'update' gives TS2322 Type '"update"' is not assignable to type '"delete" | "restore"'. The probe was removed.

Gates (local, at 13af833, each exit captured before any pipe)

  • Build: turbo over the closure of @object-ui/plugin-grid and @object-ui/data-objectstack, plus @object-ui/types (concurrency 2). Exit 0, 13 tasks.
  • type-check: @object-ui/types exit 0, @object-ui/plugin-grid exit 0, @object-ui/data-objectstack exit 0. The last is the downstream sweep: the consumers that name ImportJobUndoResult.
    • plugin-grid's tsconfig.test.json program contains the new test file (--listFiles: 1 hit). It reads packages/types/dist/data.d.ts.
    • @object-ui/app-shell names only undoImportJob: undefined, an optional method whose type did not change. It is declared to CI.
  • lint: @object-ui/plugin-grid exit 0 (0 errors), @object-ui/types exit 0 (0 errors). The warnings predate this branch.
  • pnpm check:i18n-keys 0 · check:i18n-drift 0 (0 en values changed, 3 keys added) · check:i18n-dead-keys 0 (the new keys are not listed).
  • pnpm check:spec-symbols 0 · check:spec-floors 1 (whole repo; @object-ui/types 0 findings, as above).
  • pnpm check:new-line-citations 0 (0 new citation(s)) · check:control-bytes 0.
  • check-changeset-presence 0 · check-changeset-no-major 0 · check-changeset-fixed 0.
  • check-governed-queue-guard --test over the 15 changed paths: NOT GOVERNED.
  • pnpm install --frozen-lockfile --offline 0 (Lockfile is up to date): the floor move leaves the lockfile valid.
  • The full farm is CI's.

Acceptance notes

  • Docs. No page under content/docs/** or any package README describes the import undo today. This claim's file surface does not cover docs, so none was added; that is noted here and not filed.
  • Record ids are not shown. Each failures entry also carries id, action and code. The notice shows the refusal sentence the card asks for, not the opaque record id.

Generated by Claude Code

…ys so

The undo route answers `success: false` with one `failures` entry per row
a control refused, and keeps the job undoable for those rows. The wizard
never read the answer. `useImportJobUndo` now hands a partial answer to
both lists that run Undo, which show how many rows stayed, each refusal
sentence, and that undoing again retries only those rows.

`ImportJobUndoResult` is bound to the spec's `UndoImportJobResponse` by
reference instead of a hand-written copy that lacked `failures`, and
`@object-ui/types` floors `@objectstack/spec` at 18.0.0-next.5, the first
release that carries it.

The History list keeps the undo outcome in its own slot: its reload
cleared the error slot an undo wrote to, so a thrown undo there showed
nothing either.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7
The wizard offers a background job only when the data source can read a
job's results too, so the stub carries `getImportJobResults`.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 77 chunks) 3200.9 KB 3744.1 KB
Main entry chunk (gzip) 81.0 KB 350 KB
Entry file index-Bvcnrr_z.js —
Status PASS —

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

Package Size Gzipped
app-shell (buildIdentity.js) 0.80KB 0.49KB
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.17KB 7.81KB
app-shell (runtime-config.js) 24.52KB 8.48KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 614.46KB 149.64KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 250.88KB 70.07KB
fields (index.js) 292.36KB 74.70KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.33KB 11.96KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 55.04KB 16.10KB
plugin-charts (index.js) 85.14KB 23.42KB
plugin-chatbot (index.js) 238.47KB 56.30KB
plugin-dashboard (index.js) 147.53KB 39.95KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 265.59KB 70.57KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 181.18KB 46.85KB
plugin-gantt (index.js) 178.46KB 45.13KB
plugin-grid (index.js) 272.91KB 75.73KB
plugin-kanban (index.js) 64.49KB 19.83KB
plugin-list (index.js) 140.56KB 36.26KB
plugin-map (index.js) 27.17KB 9.00KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.31KB 11.88KB
plugin-tree (index.js) 15.27KB 5.40KB
plugin-view (index.js) 94.68KB 24.36KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 126.45KB 41.40KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

… arm

`ImportJobUndoResult` now requires `failures`, so a custom DataSource
whose `undoImportJob` resolves to the old shape no longer compiles. The
changeset's BREAKING paragraph said so in prose only; the declaration
line now carries it as `Clause-②: yes (narrowing)`.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014MXhk1yjWh37d634hLy3p7
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 77 chunks) 3201.4 KB 3744.1 KB
Main entry chunk (gzip) 81.0 KB 350 KB
Entry file index-Bvz3cO1y.js —
Status PASS —

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

Package Size Gzipped
app-shell (buildIdentity.js) 0.80KB 0.49KB
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.17KB 7.81KB
app-shell (runtime-config.js) 24.52KB 8.48KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 615.08KB 149.86KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 250.88KB 70.07KB
fields (index.js) 292.36KB 74.70KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.33KB 11.96KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 55.04KB 16.10KB
plugin-charts (index.js) 85.14KB 23.42KB
plugin-chatbot (index.js) 238.47KB 56.30KB
plugin-dashboard (index.js) 147.53KB 39.95KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 265.63KB 70.60KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 181.98KB 46.95KB
plugin-gantt (index.js) 178.46KB 45.13KB
plugin-grid (index.js) 272.91KB 75.73KB
plugin-kanban (index.js) 64.49KB 19.83KB
plugin-list (index.js) 140.56KB 36.26KB
plugin-map (index.js) 27.17KB 9.00KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.31KB 11.88KB
plugin-tree (index.js) 15.27KB 5.40KB
plugin-view (index.js) 94.68KB 24.36KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 126.45KB 41.40KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4eb1072305f027d9a94ccf8076200f04299069d3
Local-runs: none
Scope: packages/types/src/data.ts, packages/types/package.json, packages/plugin-grid/src/ImportWizard.tsx, packages/plugin-grid/src/ImportWizard.undoPartial-13015.test.tsx, packages/i18n/src/locales/ar.ts, packages/i18n/src/locales/de.ts, packages/i18n/src/locales/en.ts, packages/i18n/src/locales/es.ts, packages/i18n/src/locales/fr.ts, packages/i18n/src/locales/ja.ts, packages/i18n/src/locales/ko.ts, packages/i18n/src/locales/pt.ts, packages/i18n/src/locales/ru.ts, packages/i18n/src/locales/zh.ts + .changeset/13015-undo-partial-failures.md (read for section ② only), packages/types/src/index.ts, packages/data-objectstack/src/index.ts (importJobApi, undoImportJob), packages/app-shell/src/views/identityImport.ts, packages/plugin-grid/src/ImportWizard.cancelCounts-11650.test.tsx, packages/plugin-grid/src/index.tsx, packages/i18n/src/tests/all-locales-key-parity.test.ts, packages/i18n/src/tests/count-families-11445.test.ts, .changeset/config.json, root package.json (pnpm.overrides), all at objectui origin/main; objectstack at the pin f4755a68a797: packages/spec/src/api/export.zod.ts (UndoImportJobResponseSchema, ImportJobApiContracts.undoImportJob), packages/spec/src/api/index.ts, packages/spec/src/shared/lazy-schema.ts, packages/spec/package.json, packages/client/src/index.ts (undoImportJob), packages/rest/src/data-action-routes.ts (the undo door's answer); objectstack history for the floor: commit adb7910f8, the 18.0.0-next.3 / next.4 / next.5 version-bump commits and the remote @objectstack/spec@18.0.0-next.* tags (git ls-remote --tags).

Face: the PR body and the changeset both declare Clause-②: yes (narrowing), read with readClause2Line from scripts/pm/clause2-line.mjs: PR body gives kind declared, value yes, arm narrowing; changeset at the head gives the same. The diff widens @object-ui/i18n's TranslationKeys and narrows what a DataSource.undoImportJob implementer may return. Net diff read as git diff origin/main...4eb1072 (merge-base c2191654a, 15 files, 337 insertions, 33 deletions), matching the PR's file list. Head repo is objectstack-ai/objectui (not a fork); the PR is a draft.

① Derived judgments

(a) ImportJobUndoResult becomes UndoImportJobResponse by reference — RIGHT. At the pin, UndoImportJobResponseSchema is lazySchema over a z.object factory, and lazySchema is typed to return its factory's schema type, so z.input of it is the object's own input type. Its members, all required (no .optional() on any): success boolean, jobId string, object string, deleted int, restored int, failed int, failures array of { id: string, action: 'delete' | 'restore', code: string, error: string }. The hand copy the diff deletes declared success, jobId, object, deleted, restored, failed with identical names, types and optionality and nothing else. So the only change in the published type is the one the spec declares: failures required, action closed to 'delete' | 'restore'. By reference, the alias cannot be wider or narrower than the spec at the pin; @objectstack/spec/api re-exports export.zod wholesale (export * from './export.zod'), and the spec's package.json serves ./api types from ./dist/api/index.d.mts. The dev's reverse type check (TS2741 on a literal without failures, TS2322 on action: 'update') is the same fact from the consumer side. The doc on DataSource.undoImportJob was updated to name the failures entries. packages/types/src/index.ts re-exports the alias under the same name, so no consumer import moves.

(b) Implementers of undoImportJob at origin/main — all still satisfy the type. RIGHT. (1) packages/data-objectstack/src/index.ts undoImportJob returns api.undoImportJob.call(this.client.data, jobId) where importJobApi() casts the client's data to a local structural type whose undoImportJob is declared Promise of ImportJobUndoResult, so it compiles against the alias unchanged; at runtime the client at the pin types data.undoImportJob as Promise of UndoImportJobResponse and unwrapResponse hands back the flat body, which the undo door answers as res.json({ success: reverted, jobId, object, deleted, restored, failed: failures.length, failures }) — failures already reached the wizard; only the type and the UI dropped it (the PR's H2, verified in the cited code). (2) packages/app-shell/src/views/identityImport.ts sets undoImportJob: undefined on the optional member; unaffected by the return type. (3) No other implementer: git grep undoImportJob origin/main -- packages apps examples finds only those two, the wizard's feature-detects, and test stubs. The pre-existing stub in ImportWizard.cancelCounts-11650.test.tsx returns the old shape with success: true through ImportWizardProps.dataSource: any, so it compiles, and the hook reads failures only when success is false, so it still passes (the dev's full plugin-grid run and the green CI shards agree). The new test's PARTIAL and FULL literals are typed ImportJobUndoResult and carry failures.

(c) The @object-ui/types floor ^18.0.0-next.5 — RIGHT, and it is the first published release. Read-only history in objectstack: adb7910f8 (PR objectstack-ai/objectstack#24431, the commit that added failures) is an ancestor of 820fd5aef0, the version-bump commit that set @objectstack/spec to 18.0.0-next.5 and the commit the remote tag @objectstack/spec@18.0.0-next.5 points at; it is NOT an ancestor of 2c9bf9430a, the 18.0.0-next.3 bump and that tag's commit. A 18.0.0-next.4 bump commit exists (725a5b13) and does carry adb7910f8, but the remote carries tags for next.0, next.1, next.2, next.3 and next.5 only — no next.4 tag — which matches the PR body's registry reading that next.4 was never published. So next.5 is the first published release carrying UndoImportJobResponseSchema.failures, and ^18.0.0-next.5 is the lowest correct floor. Dist resolution at that floor is judged from the PR body's evidence, not re-derived: failures: z.ZodArray reads 2 hits in next.5's dist/api/index.d.mts and 0 in next.2's, and the scoped check:spec-floors over @object-ui/types reads 0 findings at ^18.0.0-next.5. The lockfile does not move because every @objectstack/* resolves through the root pnpm.overrides to file:.objectstack-pin/* (objectui's pin f4755a68a797, whose spec package.json reads 18.0.0-next.5). @object-ui/plugin-grid keeping ^18.0.0-next.1 is acceptable: its source names ImportJobUndoResult from @object-ui/types and no spec symbol, so the floor sits on the package whose .d.ts imports UndoImportJobResponse.

(d) The @object-ui/i18n keys — RIGHT, consistent across the ten packs. en.ts gains grid.import.undoPartial, undoPartial_one, undoPartial_other, and TranslationKeys is typeof en, so the published type widens by exactly those three keys. Per pack the slot set is byte-for-byte the shape of the existing grid.import.moreErrors family in the same pack: ar base plus _zero, _one, _two, _few, _many, _other; ru base plus _one, _few, _many, _other; es, fr, pt base plus _one, _many, _other; de, en, ja, ko, zh base plus _one, _other. That is what all-locales-key-parity.test.ts computes (every en key in every pack, plus the categories Intl.PluralRules selects for the pack's own language, plus the base key a family must carry), and every value interpolates `{{count}}` while the one call site passes a number (outcome.answer.failed), which is what count-families-11445.test.ts and check:i18n-keys hold. The wizard's IMPORT_DEFAULT_TRANSLATIONS fallback carries the same three en rows and defaultsRow selects _one / _other by Intl.PluralRules('en'), so a host with no provider renders the same sentence. The retry sentence is part of the same string, so no second key is needed. Repo Gates on the head reads success (it carries the i18n gates), and the dev's check:i18n-drift reads 0 en values changed, 3 keys added.

(e) The UI — RIGHT on every point the card names. useImportJobUndo now takes the answer; success: false hands { kind: 'partial', answer } to the list that ran Undo, and the shared ImportUndoOutcomeNotice renders t('grid.import.undoPartial', { count: answer.failed }) (the count from failed, the retry sentence in the string), then a list of the distinct failures[].error sentences (a Set), capped at 10 with the existing grid.import.moreErrors, under role="alert". Undo stays offered: both lists re-read the job through onSettled and show Undo by isImportJobUndoable (undoable true, no revertedAt, not active), which the server's partial answer leaves exactly so (it stamps nothing and rewrites the undo log to the remaining rows); the History list keeps the notice in its own undoOutcome slot, which load() does not clear (it clears error only — the base defect the PR's bounded fix names), and the cancelled screen's slot is untouched by rereadFinishedJob. A full undo is unchanged: success: true sets no outcome, notifyDataChanged still fires, and the re-read job flips to "Undone". A thrown undo lands in the same slot as { kind: 'error', message } and is drawn under the former test ids (import-cancelled-undo-error) and a new one for the History list (import-history-undo-error), where the base wrote into error and lost it to the reload. The new test drives both lists with a three-row partial answer, the retry, the throw, and asserts one data change; the dev's two ablations each turned exactly the pins that depend on the deleted mechanism red.

Check-runs on the head, read 2026-10-19T19:12:13Z (36 of 36 returned): success 32 — Build & E2E, Build Docs, Bundle Analysis, Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Lint, Live E2E (informational), Repo Gates, Test, Test (dist pins), Test shards 1 through 16 of 16, label; in_progress 1 — Type Check; skipped 3 — Test (coverage), Test (coverage shard), dependabot. None failed. In-progress is recorded as what it is; the queue reads the required set itself.

② Semver level

Matches what the diff publishes. @object-ui/types: minor with a BREAKING paragraph that carries the migration (a custom DataSource whose undoImportJob resolved to the old shape must now return failures, an empty array for a full undo; @object-ui/data-objectstack already does) — the right level under this repo's release model (objectui's major follows the @objectstack family and its own breaking changes ship as minor with the breaking semantics stated; Changeset Bump Policy reads success). @object-ui/i18n: minor — TranslationKeys widens by three keys, additive. @object-ui/plugin-grid: patch — a fix; the package's public face (ImportWizard, ImportWizardProps, ImportResult from index.tsx) is unchanged and IMPORT_DEFAULT_TRANSLATIONS is not exported from the barrel. All three are members of the 40-package fixed group in .changeset/config.json, so the group moves at minor (Changeset Fixed Group Check success). The Clause-②: line reads yes (narrowing) on both carriers through readClause2Line (PR body: declared / yes / narrowing; changeset at the head: declared / yes / narrowing), the spelling clause2-line.mjs reserves for a diff that widens one surface and narrows another. objectui carries no ADR-0087 registration gate and its AGENTS.md names no disposition marker, so none is owed on this changeset.

③ Boundary flags

Every dev deviation and note on #13015 (comments 6274833740 and 6274944547), answered; neither report lists an open question.

  1. Bounded in-place fix (the separate undoOutcome slot for a thrown History undo): same hook, same claimed file, evidenced by a base probe and pinned by the third test. It changes no published surface. Accepted.
  2. Each DISTINCT refusal sentence shown once, capped at 10 with moreErrors: the card asks that each failures[].error be shown; the headline count covers every row and one hook refuses many rows alike. Within the card's "done" with the reason stated. Record ids, action and code are on the typed answer for any later surface. Accepted, not escalated.
  3. Floor raised on @object-ui/types only: judged right in ①(c).
  4. The types and i18n suites run as one repo-root vitest call: process, no contract bearing.
  5. @object-ui/app-shell type-check not run locally: its only site sets the optional member to undefined, which the alias cannot affect; CI Type Check is in progress at this reading and is the gate verdict.
  6. The plugin-grid suite backgrounded by the harness: process, no contract bearing.
  7. Attribution: all four branch commits carry the model-free pair (Co-authored-by: Claude, Claude-Session) per objectui AGENTS.md; the PR body carries the session-URL footer. No contract bearing.
  8. Round 1: the dev did not PATCH the PR body and handed the line to the seat; the live PR body now reads yes (narrowing) through the reader. Resolved. The changeset's bold wrapping was dropped; the reader tolerates either. Resolved.
  9. Out of scope, @object-ui/types: its @objectstack/spec floor (^18.0.0-next.2) is too low: the built zod face imports checkPageMainRegionWidth and checkPageRecordBindingPlacement, which next.2 does not export #13065: the floor move also clears that card's two floor-too-low findings on @object-ui/types; a side effect of the right floor, noted on that card's carrier. The PR body closes only objectui Import Wizard: an import undo that leaves rows unreverted (success: false with failures, after #24380) shows no message, and the job is still listed as undoable #13015 (Fixes #13015 is its only closing keyword).
  10. Out of scope, docs: no page under content/docs/** or package README describes import undo; outside the claim's file surface and recorded in the PR's Acceptance notes. Not a contract item; the seat may file it.
  11. Out of scope, the objectui changeset gates do not read the Clause-② line: an observation about gate scope; the arm is read by this record and by objectstack's gates on the sibling. No action.

Implemented-by: claude/issue-13015-undo-partial-failures
Reviewed-by: session_014MXhk1yjWh37d634hLy3p7

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · objectui#13015 · PR #13071 · domain:ui#1 · session_014MXhk1yjWh37d634hLy3p7 · 2026-10-19T19:13Z

Head 4eb1072305f027d9a94ccf8076200f04299069d3. Reviewed against the amended claim 6274888758 and the dev reports 6274833740 (round 0) and 6274944547 (patch round 1), read on GitHub rather than from the reports. Contract review: 6275187571 (CONTRACT_REVIEW_TIER, narrowing face, PASS, same head; an isolated subagent at that tier, adopted by this seat).

  1. Shape: draft PR against main. The body's first line is Fixes #13015, and that is its only closing keyword: the one mention of objectui#13065 says "this PR closes no card but this one", which carries no number after the verb. Clause-②: yes (narrowing) is at the start of a line in both the PR body and the changeset, read by readClause2Line as declared / yes / narrowing. Assignee huangyiirene. 15 files, +337 / −33. No governed path, and well under 3000 lines ⇒ not Tier H.
  2. Premise, re-measured: on main c219165, useImportJobUndo (ImportWizard.tsx:1103) ran await ds.undoImportJob(jobId) and never read the answer, and ImportJobUndoResult was a hand-written copy without failures. The diff reads the answer and binds the type to the spec's UndoImportJobResponse by reference.
  3. Gates: the dev's local runs on the round-0 tree (unchanged since 92728ce except the changeset line): plugin-grid 229 files / 2077 tests, i18n + types 472 files / 11870 tests, type-check for types, plugin-grid and data-objectstack, lint, all four i18n checks, spec-symbols, scoped spec-floors (0 findings), changeset checks. All exit 0. The two ablations go red on exactly their pins. The red Test on 13af833 was the patch-round push cancelling 15 shards (stale head, no action). The flip to ready waits for every check-run on 4eb1072 to be green or an expected skip (Type Check was still running at the review's reading).
  4. Accepted deviations:
    • Bounded in-place fix: a thrown undo in the History list was wiped by the list's reload. Same hook, claimed file, base probe red, pinned by the third test.
    • Distinct refusal sentences are shown once, capped at 10 with grid.import.moreErrors; the headline count covers every row.
    • The floor is raised on @object-ui/types only (the package whose .d.ts imports the spec type). next.5 is the first published release carrying failures, since next.4 was never tagged.
    • Patch round 1 (this seat's REWORK): the arm moved from widening to narrowing, because the change is compile-breaking for a custom DataSource. The dev changed the changeset, and the seat wrote the PR body's line (the dev's definition hands body edits to the seat).
    • Changeset sentences checked against the diff: a partial answer "reports how many rows were not undone and that undoing again retries only those rows", "Rows refused with the same sentence share one line, and after ten distinct sentences the rest are counted", "Undo stays offered", "A thrown undo in the History list shows its message again", ImportJobUndoResult "is the spec's UndoImportJobResponse, bound by reference", and the BREAKING paragraph with its migration. All match the code.
  5. Out of scope, where each went:
  6. Landing: not Tier H. The narrowing face's review PASS is on record for this head, so needs:contract-review is not hung. Once every check-run on this head is green, the seat flips to ready and enables auto-merge. A new head needs a new review record before it enters the queue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

2 participants