Skip to content

test(console,components): pin the last four one-key member blocks (objectui#8071 slice 7) - #8942

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-8071-member-pins-slice-7
Sep 10, 2026
Merged

test(console,components): pin the last four one-key member blocks (objectui#8071 slice 7)#8942
os-warren merged 1 commit into
mainfrom
claude/issue-8071-member-pins-slice-7

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Refs #8071 — slice 7. The card stays open, and 37 exemptions remain.

What this lands, in one commit

Every remaining MEMBER_PIN_EXEMPTIONS block whose remainder was exactly one key becomes a real per-block member pin. The four exemptions are deleted and MEMBER_PIN_EXEMPTION_CEILING moves 41 to 37 in the same commit, so the ceiling never holds an unused slot.

key pin file new or promoted
element:button.action packages/components/src/__tests__/elementButtonActionMembers-8071.test.tsx new
element:number.filter packages/components/src/renderers/basic/__tests__/elementNumberFilterMembers-8071.test.tsx new
page:accordion.items packages/components/src/__tests__/pageAccordionItemMembers-8071.test.tsx new
page:tabs.items packages/components/src/__tests__/pageTabsItemMembers-8071.test.tsx new

All four blocks now carry zero exemptions. The batch is coherent on slice 6's terms: one package (packages/components) registers all four — the two element: keys share renderers/basic/elements.tsx, the two page: keys share renderers/layout/containers.tsx — and the selection rule is stateable in a sentence.

AST ledger — measured, not inherited

TypeScript AST walk (ts.createSourceFile, VariableDeclaration, initializer unwrapped through as / satisfies / parens, PropertyAssignment), object boundaries from AST node spans. Re-derived on this branch's own head rather than taken from the dispatch.

                                 before (b97129e96)      after (4e4e6ca56)
MEMBER_PIN_EXEMPTIONS                    41                      37
MEMBER_PIN_EXEMPTION_CEILING             41  (:2747)             37  (:2818)
MEMBER_PINS                              49                      53
distinct block prefixes                  10                       6
NEWLY_JUDGED_UNPINNED_MEMBERS             2                       2   (untouched)

The delta balances on one instrument: minus 4 exemptions, minus 4 ceiling, plus 4 pins, with the parent holding exactly one key under each of the four prefixes taken. Parser guards clean: parseDiagnostics 0; exactly one VariableDeclaration per name, so no shadowed node was picked up; 0 non-PropertyAssignment members and 0 computed keys in either object; 0 non-string array elements.

Instrument cross-check, reported rather than smoothed

A key-anchored regex bounded to top-level indentation inside the AST span AGREES on both objects: 37 = 37 and 53 = 53, with an empty only-in-AST and an empty only-in-regex set.

The depth-blind key anchor slice 6 caught still reads MEMBER_PINS as 159 against the AST's 53. Fully accounted for: values are objects of shape file-plus-pins, so 53 entries carry 106 nested sub-keys, and 53 + 106 = 159 matches to the unit. That is the same failure mode for the fourth time on this file, on new content — recorded, not smoothed.

The two one-key blocks deliberately NOT taken

  • record:related_list.actions is the NO_READ_SITE_TO_PIN sentinel, and the reading was re-measured here rather than inherited: on this tree packages/plugin-detail/src/renderers/record-related-list.tsx contains zero case-sensitive occurrences of actions (control on the same file, same run: import reads 9), and the only near-matches are useRelatedRecordActions / relatedActions — the host bridge the constant already names. Nothing to pin, still. It is not "one easy key left".
  • object-kanban.columns / object-kanban.dataSource are the NEWLY_JUDGED_UNPINNED_MEMBERS pair, held by the unruled contract question in objectui#8913 and by the live branch over packages/plugin-kanban/**. Pinning a shape a pending ruling may delete would be worse than leaving it exempt, so NEWLY_JUDGED_UNPINNED_MEMBERS is unchanged — no block it names was touched, and no file this PR edits is owned by that branch.

Why four new files rather than promotions

Every candidate was read end to end before being rejected, not dismissed on its greps. None covered its key's member set: element-button-action.test.tsx drives only type and to on one navigation action; action-bodyExtra-forward.test.tsx pins one member across four renderers; page-accordion-icon.test.tsx pins icon alone; the five page:tabs suites pin one member each; and element-number.contractEnvelope-6726.test.tsx drives the same find() fallback branch but never authors a filter, so the locator itself would refuse it.

One had to be refused rather than merely passed over. action-bodyShape-forward.test.tsx NAMES element:button and the key action, so it would satisfy the locator — while what it actually says is that element:button is deliberately out of its scope. Crediting it would have been a pin asserting the opposite of its claim, which is the false-negative shape objectui#8068 exists to end. Its boundary is instead pinned as behaviour in the new file, which authors a non-whitelisted key and asserts it is dropped.

Ablations — every mutation proven on disk BEFORE any result was read

Each run anchors grep counts on both the injected and the removed text, requires git hash-object to differ from the HEAD blob, restores with git checkout HEAD -- ABSOLUTE_PATH from a trap ... EXIT INT TERM, and proves restoration by blob hash — never by an exit code.

Family 1 — the gate mechanism. One deleted exemption re-added, ceiling untouched. Blob c5497f50 to c7e10387 and back to c5497f50.

Exactly three rows red, each for its own reason, 195 green:

× judges a non-vacuous member-shape census   expected [ 'element:button.action' ] to deeply equal []
× carries no stale member-pin exemption      expected [ 'element:button.action' ] to deeply equal []
× the member-pin exemption list only ratchets DOWN   expected 38 to be less than or equal to 37

Family 2 — one per new pin, each failing for its own reason. All four pin files run together each time, so "its own reason" is observable rather than asserted.

pin ablated mutation rows red other three pins
element:button.action drop bodyExtra from the forward whitelist 1 — the whitelist-SET row green (23 passed)
element:number.filter rewrite the find() wrapper key to the flat spelling 1 — the wrapped-spelling row green (23 passed)
page:accordion.items it.collapsed === false becomes !it.collapsed 2 green (22 passed)
page:tabs.items drop the positive gate on the count badge 1 — the zero-count row green (23 passed)

Blob hashes: elements.tsx 498cbdef to e0e1ebbd and to 7fcb7bfc, restored to 498cbdef both times; containers.tsx 0ba09c75 to 072638f8 and to 8fecfe8a, restored to 0ba09c75 both times.

⭐ Worth one line for the next reader: the accordion ablation reds the two rows written as controls — "every panel stays shut when no item declares it" and the allowMultiple row — and not the row that names collapsed directly. In single mode defaultOpen[0] masks the difference between the two readings, so the controls are the only thing that catches it. They are load-bearing, not decoration.

Verification

Exit codes captured into a variable before any pipe; readings taken from each tool's own verdict line.

run result
the gate, registry-inputs-spec-parity.test.ts exit 0 — 198 passed
@object-ui/console full suite exit 0 — 98 files, 1132 tests
@object-ui/components full suite (two shards) exit 0 — 130 + 129 = 259 files, 2354 tests
type-check, both packages exit 0 — 0 error TS rows, both echoed their script and printed Done
lint, both packages exit 0 — 0 errors, 948 and 212 warnings (all pre-existing)

Notes that keep those readings honest:

  • The components shard counts sum to 259, which equals the package's actual test-file count — so the sharded run covered the package rather than half of it.
  • lint was read for the literal word error, not merely a zero exit. Every hit is a warning Error: ... rule message or a source line containing the word; zero severity-ERROR rows in either package. Both packages emitted real eslint summaries (1535 and 621 output lines), so "the filter matched no script, nothing ran, exit 0" is excluded.
  • type-check genuinely covers the changed files rather than skipping them: packages/components chains tsc -p tsconfig.test.json, whose include globs are src/ test patterns, and apps/console includes src with no exclude.
  • The first type-check attempt failed with TS2307 on unbuilt workspace siblings. The dependency closure was built and it was re-run; the table reports the run on the built tree.

.changeset/member-pins-slice-7.md carries an empty frontmatter — declared as releasing nothing, which scripts/check-changeset-presence.mjs accepts as a complete answer (exit 0). scripts/check-governed-queue-guard.mjs --test on all six paths reports NOT GOVERNED (exit 0).

After this slice

Every remaining exemption sits on one of four multi-key blocks — object-grid 15, object-form 7, object-master-detail-form 6, object-metric 6 — plus the two held keys. The "close a block outright by taking its last key" shape is now exhausted: the next slice is the first that has to take a partial block or close a multi-key one whole. That is stated in the ceiling docblock too, so the next reader does not spend the search rediscovering it.

⛔ Draft on purpose — the PM lands it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w


Generated by Claude Code

…jectui#8071 slice 7)

Converts every remaining `MEMBER_PIN_EXEMPTIONS` block whose remainder was
exactly one key into a real per-block member pin, deletes those four
exemptions, and lowers `MEMBER_PIN_EXEMPTION_CEILING` 41 -> 37 in the same
commit.

  element:button.action    -> elementButtonActionMembers-8071.test.tsx  (new)
  element:number.filter    -> elementNumberFilterMembers-8071.test.tsx  (new)
  page:accordion.items     -> pageAccordionItemMembers-8071.test.tsx    (new)
  page:tabs.items          -> pageTabsItemMembers-8071.test.tsx         (new)

All four blocks now carry zero exemptions. The batch is coherent on slice 6's
terms: one package (`packages/components`) registers all four, and the
selection rule is stateable in a sentence.

Two one-key blocks were deliberately left. `record:related_list.actions` is
the `NO_READ_SITE_TO_PIN` sentinel and the reading was re-measured rather than
inherited: `record-related-list.tsx` still holds zero case-sensitive
occurrences of `actions` (control: `import` reads 9 in the same file).
`object-kanban`'s `columns` / `dataSource` are held by the unruled contract
question in objectui#8913, so `NEWLY_JUDGED_UNPINNED_MEMBERS` is unchanged.

No pre-existing file covered any of the four keys' member SET; each candidate
was read end to end before being rejected. `action-bodyShape-forward.test.tsx`
had to be refused rather than passed over — it names both the block and the
key, so it would satisfy the locator, while what it says is that
`element:button` is deliberately out of its scope. Its boundary is pinned as
behaviour in the new file instead.

Ledger (TypeScript AST walk, object boundaries from node spans):
exemptions 41 -> 37, ceiling 41 -> 37, pins 49 -> 53, prefixes 10 -> 6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3488.4 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-BgAMgslR.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 (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 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.68KB 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) 500.20KB 114.67KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 28.04KB 9.46KB
data-objectstack (index.js) 201.97KB 56.22KB
fields (index.js) 246.97KB 62.30KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.54KB 46.34KB
plugin-dashboard (index.js) 132.41KB 34.84KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.19KB 65.62KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.96KB 40.93KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.58KB 16.47KB
plugin-list (index.js) 112.54KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
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.66KB 3.50KB
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) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 2.71KB 1.35KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 0.20KB 0.18KB
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) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

@os-warren
os-warren marked this pull request as ready for review September 10, 2026 06:34
@os-warren
os-warren added this pull request to the merge queue Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

⛔ The enqueue is not taking from this seat — this PR needs a human "Merge when ready" click

domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. The PR itself is fine. This is an instrument failure on my side, reported rather than worked around.

What is measured

reading value
head 4e4e6ca56752879350bc605f699b4ccb2d2133f2
checks 33 — 30 success, 3 skipped by design, 0 failure, 0 in progress
draft false (flipped by this seat at 06:34)
mergeable_state unknown — GitHub had not computed mergeability
enable_pr_auto_merge calls two — 06:34:49Z and ~07:15Z
updated_at after the first call frozen at 06:34:49Z for 40 minutes
repo-wide merges since #8911 at 06:47:06Z none, for 47 minutes — the queue is idle, ⛔ not backed up

⇒ Two calls, an idle queue, and no state change. The first call did nothing; the second has had ~19 minutes against an empty queue and also shows nothing.

Why I cannot tell you more than that

enable_pr_auto_merge returns a content-free string — "Auto-merge enabled … (method: , enabled at )" with both fields empty — and this session's GitHub MCP server exposes no issue-timeline / PR-events tool, and pull_request_read's get does not surface an auto_merge object. ⇒ There is no reading available to me that distinguishes "enqueued" from "no-op". I have reported every enqueue this shift as NOT VERIFIED for exactly this reason; this is the first one where the no-op is demonstrable.

⚠️ One plausible mechanism, stated as a hypothesis and ⛔ not as a measurement: both calls may have landed while mergeable_state was unknown. The tool's own contract says it "Fails gracefully … if the PR is already mergeable (clean status)", so it is sensitive to that state — and every enqueue that DID work this shift (#8872, #8933) was called on a PR reading unstable, not unknown or clean. I have not been able to confirm this, so it is a lead, not a finding.

⛔ What I am deliberately not doing

Not calling enable_pr_auto_merge a third time. Two no-ops against an idle queue is a broken instrument, not bad luck, and repeating it produces no new information.

Not calling merge_pull_request to force it. In this repository the merge queue is how work lands — every merge this shift shows the queue's 16–18 minute rebuild gap — and taking a direct merge is not this seat's action to take, least of all to route around my own broken tooling.

Not pushing an "update branch" commit to shake the state loose: that would move the head, discard 33 green checks and cost a full CI cycle to fix a problem that is not in the PR.

⇒ The unblock

A human "Merge when ready" click on this PR. It is green on every check, the base is only one commit behind (b97129e96; main has since taken only #8911, a deps fix with no overlap), and the diff is six files of test pins plus a changeset with an empty frontmatter — it releases nothing.

⚠️ One thing worth checking on the click, given what PR #8865 just taught: this repo's queue rebuilds each PR on current main and ejects it if the rebuild goes red, and mergeable_state cannot see that because it is computed against the PR's own base. For this PR the risk is low — main's only new commit since this base is a dependency fix touching no file here — but if it does eject, the cause will be on the rebuilt tree, ⛔ not in these six files.


Generated by Claude Code

Merged via the queue into main with commit aeaa0f6 Sep 10, 2026
35 checks passed
@os-warren
os-warren deleted the claude/issue-8071-member-pins-slice-7 branch September 10, 2026 08:09

Copy link
Copy Markdown
Collaborator Author

⛔ RETRACTION — my previous comment was wrong. The enqueue worked; nothing was broken.

domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. This retracts comment 5614884861 ("the enqueue is not taking from this seat — this PR needs a human 'Merge when ready' click").

This PR merged at 2026-09-10T08:09:14Z, in the same batch as #8884. No human intervention was needed, and none of the three things I said were "not working" were broken.

What I got wrong, and why

I concluded "no-op" from three readings:

what I read what I inferred why the inference was wrong
updated_at frozen at the first call for 40 min the call did nothing updated_at does not move when a PR sits in a queue
mergeable_state: "unknown" GitHub could not act on it it resolved on its own; it was a transient lazy-computation state, not a blocker
no repo-wide merges for 47 min after #8911 the queue is idle, so nothing is queued the load-bearing error. The queue was quiet, then resumed: #8916 + #8901 at 07:52:20Z, then #8942 + #8884 at 08:09:14Z

The actual mistake is a calibration error. Every merge I had observed this shift landed ~16–18 minutes after its enqueue, and I treated that as the queue's ceiling rather than as four samples from a distribution whose upper tail I had never measured. The true latency here was ~54 minutes from the second call. "Elapsed time exceeds every latency I have seen" is not evidence of a no-op — it is evidence that my sample was small.

⚠️ And a second over-claim in the same comment: I wrote that the first call was "demonstrably a no-op". That is not established either. This PR merged once; ⛔ nothing in what I can read distinguishes which of the two calls carried it. It may well have been the first, with the second a harmless duplicate.

What stands from that comment

Only the tooling limitation, which is unchanged and was already stated as such: enable_pr_auto_merge returns a content-free string ("method: , enabled at ", both fields empty), and this session's GitHub MCP exposes no issue-timeline / PR-events tool, so an enqueue still cannot be verified — it can only be inferred from the outcome. ⇒ The correct discipline is to keep reporting it as NOT VERIFIED and to wait for the outcome, ⛔ not to declare the instrument broken because the outcome is slow. That is the rule I broke here.

⛔ No human click was needed. Sorry for the noise on this thread.


Generated by Claude Code

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants