Skip to content

test(app-shell): serve batch 5's three app-shell probes from doubles (objectui#7307) - #8053

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-7307-network-escapes-batch5
Sep 6, 2026
Merged

test(app-shell): serve batch 5's three app-shell probes from doubles (objectui#7307)#8053
baozhoutao merged 2 commits into
mainfrom
claude/issue-7307-network-escapes-batch5

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Part of #7307 — batch 5 of the network-escape burn-down (batch 1 was #7999, batch 2 #8013, batch 3 #8019, batch 4 #8032).

The three takeable app-shell rows now serve their probes from recording
doubles, and their lines leave KNOWN_ESCAPES and PINNED_LEDGER in the same
commit. Unlike batch 4, these three are three different endpoint families with
three different readers
, so each gets its own route table and its own reasoning
about what answer keeps its assertions' meaning.

Session for this run, as a code span so it survives a body edit:
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr.

Per file

file endpoint reader it escaped through double answer chosen
src/views/metadata-admin/inspectors/FlowNodeInspector.inactiveRetained.test.tsx GET /api/v1/meta/object FlowReferenceField.tsx:389MetadataClient.list (metadata-client.ts:764) → the authenticated wrapper's bare await fetch(...) (packages/auth/src/createAuthenticatedFetch.ts:144) installMetaObjectDouble() in a module-scope beforeEach empty registry, { type, items: [] }
src/views/studio-design/StudioDesignSurface.designerRegistryMissing.test.tsx GET /api/v1/automation/_status StudioDesignSurface.tsx:3797, a bare global fetch in a mount effect, no apiFetch seam installStatusDouble() in a module-scope beforeEach empty runtime roster, { data: { flows: [] } }
src/views/studio-design/__tests__/studioSurfaceContext.test.tsx POST /api/v1/ai/conversations and GET /api/v1/ai/conversations/THE_MINTED_ID useChatConversation.ts:609 (createConversation) from the resolve effect at :828 / :864; the resume leg is fetchConversation at :599 installConversationsDouble() in a module-scope beforeEach one empty ServerConversation ({ id, messages: [] }), minted once and resumed thereafter

Mechanism, measured rather than assumed. A trap-guarded stack probe injected
at the guard's own attribution point (a new Error('probe').stack beside
pending.push(escape)), restored by blob hash afterwards, attributed every escape
in all three files. Its readings, against the dispatch's un-traced guesses:

  • /api/v1/meta/object — the dispatch offered AiChatPage.tsx:1528,
    ExcelImportBar.tsx:79, AppSidebar.tsx:253 or MetadataService.ts as
    candidates. None of them. The inspector renders FlowReferenceField for
    every reference-kind key on the selected node, and that field resolves combobox
    options through useMetadataListOptionsMetadataClient.list(type). The
    file's own vi.mock of useObjectFields does not cover it.
  • /api/v1/automation/_status — the dispatch had located no reader at all and
    suggested it might live in another package. It is in app-shell after all,
    inside StudioDesignSurface.tsx itself: the effect that turns per-flow runtime
    state into the rail's status dots.
  • /api/v1/ai/conversations — the dispatch named
    hooks/useConversationList.ts. Falsified. The reader here is
    hooks/useChatConversation.ts, and it is not a list read at all: it is the
    resolve effect MINTING a thread with a POST. That difference is what produced
    the second route below.

Escaped URLs were exactly one per family, with no second endpoint before the
doubles went in. The probe was reverted with git checkout HEAD -- path; the
guard is byte-identical to HEAD afterwards (blob back to 4205b9ff,
git diff HEAD empty).

The doubles are batch 1/2/3/4's shape verbatim: vi.stubGlobal('fetch', router) in a module-scope beforeEach, cleanup() before
vi.unstubAllGlobals() in afterEach (the #7439 ordering). Each is a router,
not a sink — it records every URL it is handed and its afterEach fails on any
URL outside the routes it serves. All three carry a headers: new Headers() on
the answer, which is part of the answer rather than decoration for file 1: the
authenticated wrapper reads response.headers.get('set-auth-token') on every API
call before the caller ever sees the body.

In files 1 and 2 the new afterEach replaces the bare afterEach(cleanup)
that used to stand there — cleanup() is still called, from inside it, ahead of
the unstub. That is stated in the comment at each site so no reader reads it as a
dropped teardown.

Why each answer keeps every assertion's meaning:

  • meta/object — empty. The failing request landed in
    useMetadataListOptions's .catch, which sets { options: [], loading: false }.
    An empty registry produces the identical state through .then. A seeded one
    would put options into every reference combobox in the tree. Nothing in the file
    asserts anything about reference options: the 11 cases assert the
    inactive-retained notice, its wording, its data-inactive-retained attribute,
    the clear button, and that onPatch is not called on render.
  • automation/_status — empty. The effect turns each returned row into a
    status DOT on the flow rail; the failing request left flowStatus at {} with
    no dots. A seeded roster would have added a dot for nightly to the very
    Automations tableau the file pins. The four cases assert registry emptiness with
    a must-hit control, plus the "no designers are registered" copy and the absence
    of the retired "click a node" instructions.
  • ai/conversations — one empty thread, and TWO routes. This is the batch's
    one real surprise, and it was measured, not reasoned: serving only the mint made
    this file's OWN router assertion go red naming
    /api/v1/ai/conversations/conv_studio_copilot. A mint that SUCCEEDS is
    resumable, useChatConversation caches the minted id in localStorage, and
    happy-dom keeps that store for the whole file — so case 1 mints and cases 2 to 4
    resume. Both routes answer the same empty ServerConversation, so the fake
    server is self-consistent: one thread, minted once, resumed thereafter. Empty
    rather than seeded because a seeded thread would hydrate initialMessages into
    the capture stub for no assertion's benefit. The file asserts exactly one thing
    per case — the surfaceContext prop the pane receives, derived from the URL
    alone — ChatPane is a capture stub, and the resolve settles in a microtask
    AFTER each synchronous case body has already read capturedProps.

No file's assertions depend on the request FAILING, so there is no fork to report.
Nothing is skipped, quarantined or silenced; the real request stayed the evidence
until the double replaced it.

Ledger arithmetic

4 to 1, in both lists. The three names are deleted from KNOWN_ESCAPES in
vitest.setup.network-escape-guard.ts (each with its endpoint comment) and from
PINNED_LEDGER in scripts/__tests__/network-escape-ledger.test.ts, in one
commit. Checked in lockstep by diffing the quoted paths of the two literals
against each other on the final head: empty diff, both at 1. The pin's
non-vacuity floor is length greater than 0 and KNOWN_ESCAPES.size greater than
0, so 1 clears both.

The one remaining row is
packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.specKeys.test.tsx
(/api/v1/meta/object). It is deliberately untouched: draft PR #7685 holds that
file and is still open (re-checked on this run — open, draft: true, untouched
since 05:26Z). It is a one-file batch 6.

No prose count moved. Every "21" in the two ledger files is a provenance claim
about the original sweep on 67dadd6, not a live count — re-checked line by line,
the same reading batches 3 and 4 recorded.

Evidence

Per file, before then after. "Before" for files 2 and 3 is read off the
stack-probe run, which only ADDS stderr lines and cannot change the escape or
socket counts; file 1 also has a clean pre-probe baseline that agrees (1 / 3).

file before (attribution / ECONNREFUSED lines) after, on the final head
FlowNodeInspector.inactiveRetained 1 / 3 0 / 0, exit 0, Tests 11 passed (11)
StudioDesignSurface.designerRegistryMissing 1 / 3 0 / 0, exit 0, Tests 4 passed (4)
studioSurfaceContext 4 / 20 0 / 0, exit 0, Tests 4 passed (4)

Test counts are unchanged in every file — the doubles add no cases and remove
none.

The dispatch-named scope on the final head e56f739d3:

pnpm exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/ packages/app-shell/src/views/studio-design/ scripts/__tests__/network-escape-ledger.test.ts
— exit 0, Test Files 120 passed (120), Tests 1083 passed | 1 skipped (1084),
316s. It is not zero-escape, and that is expected and stated in the dispatch:
its 1 remaining attribution line names FlowNodeInspector.specKeys.test.tsx, the
row still on the ledger, and its 1 ECONNREFUSED line is that same escape. Zero
lines in the run name any of this batch's three files.

The whole-package app-shell suite is declared as CI's rather than run here: it
took 918s for batch 4 and would exceed this container's foreground cap under
concurrent load. The scope above is the dispatch-named one and covers every file
this diff touches plus the pin.

Ablation

Run on the final head against FlowNodeInspector.inactiveRetained (3 describe
blocks, 11 cases — the most of the three), with its KNOWN_ESCAPES and
PINNED_LEDGER lines still deleted:

  • mutation proven ON DISK before the run: installMetaObjectDouble occurrences
    2 to 0, bare afterEach(cleanup); 0 to 1, blob 86b5c8f8 to ad794f14
    (a hash comparison, not an editor exit code);
  • result: exit 1, Tests 1 failed | 10 passed (11), and the guard's red names
    the file, the socket and the case:
    Network escape: this test reached a REAL socket at http://localhost:3000/api/v1/meta/object,
    file: packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.inactiveRetained.test.tsx,
    test: the affordance appears exactly when a value is hidden-but-stored > does NOT appear on a node with no gated fields at all;
  • restored by git checkout HEAD -- path from inside the script's
    trap ... EXIT INT TERM: blob back to 86b5c8f8, git diff HEAD empty,
    git status --porcelain empty.

The direction is the plain one — red — and it is the direction that matters here:
it proves the three deletions are backed by real doubles rather than by luck.

No dist preflight leg was needed, re-confirmed on the first run as the
dispatch asked: the ablation edits the test file itself, which Vite transforms
directly, so there is no dist/ between the mutation and the assertion.

Gates

All on the final head e56f739d3, exit codes captured by redirect-then-capture.

gate result
dispatch-named vitest scope exit 0 — 120 files, 1083 passed, 1 skipped
per-file runs, all three exit 0, 0 attribution / 0 socket lines each
node scripts/check-changeset-presence.mjs exit 0 — 3 source files of 1 released package, 1 changeset, EMPTY frontmatter accepted as the explicit exemption
pnpm check:control-bytes exit 0 — 6460 tracked text files scanned
grep -naP control-byte self-scan over all 6 changed paths no hits (grep exit 1)
node scripts/check-governed-queue-guard.mjs --test (all 6 paths) exit 0 — NOT GOVERNED, 6 paths against 5 governed surfaces, none matched
pnpm type-check:scripts exit 0
pnpm type-check:vitest-setup exit 0 after the build. On the unbuilt tree it was PREREQUISITE NOT MET exactly as the dispatch predicted — 4 TS2882 side-effect-import errors in vitest.setup.dom.tsx, none about this diff
pnpm check:vi-mock-inherit exit 0
pnpm check:vi-mock-specifiers exit 0
turbo run type-check lint --concurrency=2 --force --filter @object-ui/app-shell exit 0 — 32 tasks, 0 cached, 5m02s

Non-vacuity, both proven rather than asserted:

  • tsc -p packages/app-shell/tsconfig.test.json --listFiles — 4421 files in the
    program, and each of the three edited test files appears exactly once.
  • eslint . --format json in packages/app-shell — 1087 files reported, each of
    the three edited files present, 0 errors repo-package-wide. The two warnings
    on FlowNodeInspector.inactiveRetained are pre-existing
    @typescript-eslint/no-explicit-any hits on the file's own
    as any casts, at lines 104 and 144 before this change and 177 and 217 after —
    the same two, shifted by the 73 lines added above them.

Live E2E (informational) is red on every branch today for an upstream reason
(#7990 / objectstack#16186) — not this PR's.

Concurrency

Branched from 8d40c18a7. main moved to 58cd01a38 mid-run (#8034, #8036,
#8038, #8040), so origin/main was merged in and every gate above was re-run on
the merged head
e56f739d3 — the numbers quoted are all post-merge. The
incoming diff is disjoint from this one: it touches plugin-charts,
packages/types/README.md, two content/docs pages, scripts/check-doc-snippet-types.mjs
and packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx, none of
which this batch reads or writes. Draft PR #7685 still holds
FlowNodeInspector.specKeys.test.tsx, which is why that row is excluded here.

No labels, assignee or PR state were written by this run; the identity is the
branch named in the batch-5 claim comment.


Generated by Claude Code

…(objectui#7307)

Three of the four remaining network-escape rows now answer from recording
doubles instead of a real socket, and their lines leave both KNOWN_ESCAPES
and PINNED_LEDGER in lockstep (4 -> 1 on each side).

Traced at the guard's attribution point rather than guessed:

  - FlowNodeInspector.inactiveRetained -> FlowReferenceField.tsx:389 ->
    MetadataClient.list -> GET /api/v1/meta/object, answered as an empty
    `{ type, items: [] }` registry;
  - StudioDesignSurface.designerRegistryMissing -> StudioDesignSurface.tsx:3797
    -> GET /api/v1/automation/_status, answered as an empty
    `{ data: { flows: [] } }` roster;
  - studioSurfaceContext -> useChatConversation.ts:609 ->
    POST /api/v1/ai/conversations, answered as one empty conversation, plus
    the GET resume route the hook's localStorage cache makes cases 2-4 take.

Each double is a router, not a sink: it records every URL and its afterEach
fails on any URL outside the routes it serves; cleanup() runs before
vi.unstubAllGlobals() (objectui#7439 ordering).

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

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3186.0 KB 3191.4 KB
Main entry chunk (gzip) 143.5 KB 350 KB
Entry file index-DuMkzIKe.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) 15.67KB 5.75KB
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) 5.13KB 2.35KB
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) 497.06KB 113.79KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 182.08KB 50.62KB
fields (index.js) 242.44KB 61.25KB
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) 4.28KB 1.75KB
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) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.43KB 19.71KB
plugin-chatbot (index.js) 193.53KB 46.05KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.59KB 63.48KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.56KB 56.63KB
plugin-kanban (index.js) 52.30KB 14.49KB
plugin-list (index.js) 113.24KB 27.66KB
plugin-map (index.js) 20.35KB 6.77KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 29.95KB 8.67KB
plugin-tree (index.js) 9.19KB 3.19KB
plugin-view (index.js) 84.33KB 20.75KB
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) 81.07KB 26.86KB
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) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 10.35KB 3.60KB
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.74KB 1.41KB
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 (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
Contributor Author

Standing down on Live E2E (informational): that check is red on main itself today (base-red, anchored on objectui#7990 / objectstack-ai/objectstack#16186), it is informational (not required), and this PR changes three app-shell unit-test files, the network-escape ledger pair and an empty changeset — nothing the live lane exercises. No fix is ported because none exists yet upstream. The flip to ready happens only once every non-informational check on e56f739d3 is green.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 13:28
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 65ce8c5 Sep 6, 2026
33 of 34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7307-network-escapes-batch5 branch September 6, 2026 13:44
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