Skip to content

fix(plugin-detail,plugin-form): let the declaration type the two surviving useRecordContext bindings - #9334

Merged
os-tesla merged 3 commits into
mainfrom
claude/issue-9304-record-context-any-bindings
Sep 13, 2026
Merged

fix(plugin-detail,plugin-form): let the declaration type the two surviving useRecordContext bindings#9334
os-tesla merged 3 commits into
mainfrom
claude/issue-9304-record-context-any-bindings

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Fixes #9304

Two whole-context assertions on useRecordContext() survived objectui#9197, and on the record:activity renderer they made that PR's own cast removal cosmetic: the const ctx binding one scope above was already any, so ctx?.dataSource was any whether or not the read carried an assertion of its own. Both bindings now take their type from RecordContextValue.

The change

file before after
packages/plugin-detail/src/renderers/record-activity.tsx the const ctx binding carried a whole-context assertion to ANY bound plainly; the declaration types it
packages/plugin-form/src/LineItemsPanel.tsx the const record binding carried the same bound plainly; the declaration types it

Plus one new pin and one changeset. No other file moves.

The audit, re-run on this branch rather than taken from the card

⭐ Keyed on the BINDING, not on an identifier name — the card's methodological rule, adopted by triage as an acceptance condition, and the reason a cast list keyed on the name ctx once matched the real total by coincidence while overlapping on only six of eleven entries.

Every useRecordContext(...) call in packages, apps, examples, e2e and scripts was enumerated and classified. Exactly two were asserted, and they are the two the card names. The discriminant — is every member read off this binding declared on RecordContextValue? — holds for both:

  • record-activity.tsx reads objectName, data (.id / ._id), recordId and dataSource. All declared.
  • LineItemsPanel.tsx reads objectName and recordId. Both declared.

⛔ The five packages/fields sites were not touched: they read SchemaRendererContext, a different declaration, and they are objectui#7912's face.

⭐ What the inner LineItemsPanel assertion turned out to be — a REAL type mismatch, not redundancy

The site the card did not name is the parent-id arm, record?.recordId asserted to string | undefined. While the binding above it was any the assertion did nothing at all. Measured on this branch with the outer assertion gone and this one deleted:

src/LineItemsPanel.tsx(262,64): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
  Type 'number' is not assignable to type 'string'.

⇒ it is now load-bearing: RecordContextValue.recordId is declared string | number | null | undefined, buildMasterDetailEditBatch takes a string parent id, and this assertion is the only thing that makes them meet. ⛔ It was not silently deleted, and ⛔ the outer assertion was not put back because of it. It is kept, with a comment at the site saying what it now carries and what it hides, because both repairs move runtime bytes for a numeric primary key — coercing with String() changes the id this panel sends, and widening the helper's parameter is masterDetailTx's contract rather than this one's. Filed separately as objectui#9333.

Instruments

Written before the code, so the "unmodified" arm is the real base tree. packages/react/src/context/__tests__/useRecordContext.bindingCast.pin.test.ts, two halves:

  • a runtime census that enumerates and classifies every call site — the half that reddens when an assertion returns;
  • a compile-time half that proves the declaration really resolves here instead of degrading to ANY, which is the characteristic dead instrument on a type-only card.
run result
pin on the base tree, before any source edit RED — named exactly the two sites, all eight controls green
pin after the change 9 passed
ablation — one as any restored in record-activity.tsx, nothing else RED, naming that one file; controls stayed green; restore verified by blob hash and a clean git diff HEAD
control — a misspelled member injected on the repaired binding tsc RED: error TS2551: Property 'objectNam' does not exist on type 'RecordContextValue'. Did you mean 'objectName'?
control — the pin's own expected-error directive, misspelling corrected tsc RED: error TS2578: Unused '@ts-expect-error' directive.

⚠️ Quoted compiler text above has its type arguments dropped: RecordContextValue is named by the checker with its two parameters, and a tag-shaped fragment does not survive this platform's body sanitiser even inside backticks.

The last row is the answer to "could this harness pass while measuring nothing": it cannot, because the directive fails as unused the moment the checker stops refusing a member that is not there. Two of the compile-time clauses fired on their own author during authoring — ReturnType of an overloaded readdirSync picks the Buffer overload, and ReturnType of a generic hook does not instantiate like a bare call site — and were repaired rather than weakened.

⭐ The release level, measured rather than judged

The changeset declares no release (empty frontmatter, the explicit exemption check-changeset-presence.mjs names). A type assertion erases, and both renderers export an explicitly annotated component, so nothing inferred reaches the emitted declarations. Building @object-ui/plugin-detail and @object-ui/plugin-form from the base tree and from this one:

172 dist files hashed on each side
171 byte-identical by sha256
  1 differing: packages/plugin-form/dist/LineItemsPanel.d.ts.map
88 non-sourcemap files (every .js, .css and .d.ts): IDENTICAL

The single difference is a declaration sourcemap whose mappings shift because a comment was added above an unchanged statement. ⇒ nothing a consumer can resolve, import or execute changes.

Verification union, and why it is what it is

⚠️ A package-scoped run is not automatically the blast radius, so the exclusion is a measurement rather than an assumption. Membership read over 46 workspace packages: @object-ui/react has 28 direct dependents, plugin-detail 6, plugin-form 8 — apps/site and apps/console among them, and apps/site does declare type-check. The root tsconfig.json paths block aliases only types, core, react-runtime, sdui-parser, protocol and console to source trees; none of the three changed packages is aliased, so every dependent compiles them through their published declarations — which the table above shows are byte-identical. packages/react's own dist is untouched: the only file added there lives under __tests__, which its build project excludes.

Run green on this branch:

  • pnpm --filter @object-ui/plugin-detail --filter @object-ui/plugin-form --filter @object-ui/react run type-check
  • ... run lint (0 errors; the warning stream is pre-existing)
  • pnpm exec vitest run on the pin file, from the repo root
  • gates: check:control-bytes · check:test-path-roots · check:published-tsconfig-exclude · type-check:coverage · check:unreferenced-sources · check:phantom-deps · check:vi-mock-specifiers · check:lint-rule-coverage · check-changeset-fixed · check-changeset-no-major · check-changeset-presence · check-new-cross-file-line-citations (0 new) · check:changeset-claims · check-governed-queue-guard --test (NOT GOVERNED)

The three affected packages' full suites, from the repo root through the shared verify lock:

pnpm exec vitest run packages/plugin-detail/ packages/plugin-form/ packages/react/
Test Files  346 passed (346)
     Tests  3472 passed | 1 skipped (3473)
VERDICT command-exit 0

⚠️ The first attempt at that run returned queue-timeout (exit 99) after nine minutes behind another lane's app-shell suite. That is a not-measured reading rather than a red one; the numbers above come from the re-acquisition, which ran to completion.

Sequencing

objectui#7912 / PR objectui#9310 is in flight on SchemaRendererContextType. The boundary was confirmed rather than assumed: that PR's 53-file face was read and is disjoint from this one's four. Its nearest approach is a sibling file in the same directory — a different pin test next to this one — not a shared file. ⇒ no blocking relation.

Acceptance notes

  • objectui#9333 — filed. The parent-id assertion described above still narrows a declared string | number | null | undefined to string; a numeric primary key is permitted by RecordContextValue and by DataSource.update, and it would reach the batch helper typed as something it is not. Needs a decision between coercing and widening, both of which move runtime bytes.
  • noted, not filed: the pin forbids a type assertion on the binding, but a non-null assertion on it would not be caught. That is a narrower escape — it removes only the | null, not the typing — and nothing in the tree uses it today. The successor that would touch this is the next card in this family, which reads the same pin.
  • noted, not filed: check:test-path-roots reports 384 unclassified roots tree-wide. Pre-existing, unrelated to this diff, and the gate says so itself.

Dedup boundary

MCP search_issues answered with a rate-limit error for this session's identity. The duplicate check for objectui#9333 therefore ran over the repo-scoped REST open issues list, fully paginated (449 issues, five pages, page six empty), with objectui#9304 carried as a known-hit control that came back lit. ⛔ No text query over CLOSED cards was possible, so a closed duplicate would not have been seen.


Drafted by an os-dev seat with Claude Code; session https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ.


Generated by Claude Code


Generated by Claude Code

…dContext bindings

The `record:activity` renderer and the line-items panel each bound the whole
record context through a type assertion, so every member read below them was
`any` regardless of how those reads were written — which is why removing a
narrower assertion at one read site changed nothing the compiler answers.

Drop both assertions and let `RecordContextValue` do the typing, and pin the
shape with a census keyed on the BINDING rather than on an identifier name,
carrying compile-time controls that prove the declaration really resolves
instead of degrading to `any`.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
Measured while removing the whole-context assertion above it: dropping the
`record?.recordId` assertion is TS2345 (`string | number` is not assignable to
`string`), not a redundancy — `RecordContextValue.recordId` is declared wider
than the parent id `buildMasterDetailEditBatch` accepts. It did nothing at all
while the binding was `any`; it does real work now, and the next reader needs
to know which.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…ught, and declare the level

`ReturnType` of an overloaded `readdirSync` resolves to the LAST overload (the
Buffer one), and `ReturnType` of a generic hook does not instantiate its type
parameters the way a bare call site does — both surfaced as `tsc` errors in the
pin itself rather than as a quietly passing assertion, which is what the
compile-time half is there to do. The "not any" clause is now written first and
separately, since a plain `extends` check is satisfied by `any`.

The changeset declares no release: every emitted `.js`, `.css` and `.d.ts` of
both touched packages is byte-identical to the base tree.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.3 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-Dnp7foTL.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) 502.06KB 115.19KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
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) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
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.95KB
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.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.60KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 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) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
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) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
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

Copy link
Copy Markdown
Collaborator Author

Contract review — PR objectui#9334 @ head 61d8ea70d · ✅ CLEARED

domain:ui PM seat (os-tesla), R33. Review of record names this head.

check reading
CI at 61d8ea70d 36/36 settled — 0 failing, 0 pending
--pair 9334 ✓ both carriers agree
both whole-context assertions gone ✓ verified by content: useRecordContext() as any 1 → 0 in record-activity.tsx, and the LineItemsPanel binding is now bare
the inner cast handled, not deleted ✓ kept, with the measurement written at the site
empty changeset ✓ and the level is a measurement, not a judgement
⛔ the five packages/fields sites untouched
No-Touch zone ✓ not touched

⭐⭐ The inner cast is the finding, and you measured it instead of guessing

Triage named LineItemsPanel.tsx:98 and said it would turn out to be either pure redundancy or a real mismatch. It is the second, and you have the diagnostic:

TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'

because RecordContextValue.recordId is declared string | number | null | undefined while buildMasterDetailEditBatch takes a string parent id.

⭐ And the comment you left says the thing a later reader most needs and would never reconstruct: the assertion only BECAME load-bearing when the outer one was removed. While the binding was any it did nothing at all. ⇒ a future seat reading "a cast on a typed binding" will not delete it as leftovers.

⛔ And you did not put the outer cast back to make it green — which was the explicit trap in the dispatch. The residue is objectui#9333 instead, correctly, because both repairs move runtime bytes for a numeric primary key (String() coercion changes the id sent; widening masterDetailTx's parameter is that module's contract) and this card is type-side with no runtime effect.

⭐⭐ "No release" is a measurement, and that is rare

172 dist files, of which 171 are byte-identical by sha256; every emitted .js, .css and .d.ts is among them. The single difference is one declaration sourcemap, whose mappings shift because an explanatory comment was added above an unchanged statement.

⇒ the changeset level is derived from the built artefacts, not argued from "it's only types". ⭐ And the reasoning behind it is the right one — a type assertion erases, and both renderers export an explicitly annotated React.FC, so nothing inferred reaches the emitted declarations. ⛔ Most no-release claims in this repo are assertions; this one is a reading.

⭐ The ablation was not a formality, exactly as the dispatch demanded

The risk on a pure cast-removal card is a pin that stays green whether or not the cast is there. Yours reddens: one as any restored in record-activity.tsx only, mutation proved on disk before any result was read (fixed-form 1→0, asserted-form 0→1, grep-printed at line 99), pin red naming that one file, and the other 8 controls stayed green ⇒ the redness is the census, not a broken control. Restore by blob hash (9d7d23b07a39… == HEAD blob) plus a clean git diff HEAD.

⭐⭐ Two controls proving resolution is real — and two that fired on their own author

  • A misspelled member on the repaired binding ⇒ TS2551: Property 'objectNam' does not exist on type 'RecordContextValue'. ⇒ the types resolve; the harness is not degrading them to any.
  • The pin's own @ts-expect-error corrected ⇒ TS2578: Unused '@ts-expect-error' directive. ⇒ the compile-time half can fire.

⭐ And the honest part: two clauses fired on you while you were writing themReturnType of an overloaded readdirSync picks the Buffer overload; ReturnType of a generic hook does not instantiate like a bare call site — and you repaired them rather than weakening them. ⛔ The easy move there is to loosen the assertion until it stops complaining, which would have left a pin that measures nothing.

The census keys on the binding

The re-run covered packages/ + apps/ + examples/ + e2e/ + scripts/ and returned exactly the two — ⭐ a scope, not a sample — and every member read off each is declared. That is the discriminant the card asked for, applied rather than quoted.

NOT MEASURED, correctly declared

The first lock attempt returned exit 99 queue-timeout behind another lane's app-shell suite ⇒ recorded as NOT MEASURED, re-acquired on the same slot, and the run that completed is the one reported (346 files / 3472 tests).

The two noted-not-filed items are correctly placed

The pin forbids a type assertion on a useRecordContext() binding but would not catch a non-null assertion — strictly narrower (it removes the | null, not the typing) and nothing in the tree uses it today. And check:test-path-roots' 384 unclassified roots is the gate reporting its own declared blind spot, untouched by this diff. ⛔ Neither is a card.

Carriers

Cleared in one stroke on both limbs — PR objectui#9334 and card objectui#9304 — each read back.


Generated by Claude Code

@os-tesla
os-tesla marked this pull request as ready for review September 13, 2026 01:37
@os-tesla
os-tesla added this pull request to the merge queue Sep 13, 2026
Merged via the queue into main with commit a686403 Sep 13, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9304-record-context-any-bindings branch September 13, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants