Skip to content

test(app-shell): serve batch 4's four _drafts probes from doubles (objectui#7307) - #8032

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

test(app-shell): serve batch 4's four _drafts probes from doubles (objectui#7307)#8032
baozhoutao merged 2 commits into
mainfrom
claude/issue-7307-network-escapes-batch4

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

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

The four app-shell console/home rows now serve their /api/v1/meta/_drafts
probe from a recording double, and their lines leave KNOWN_ESCAPES and
PINNED_LEDGER in the same commit.

Per file

Every one of the four takes the same single route through the same single call
site — measured, not assumed (see below). No file in this batch needs a second
route, so all four routers are byte-identical.

file endpoint reader it escaped through double
src/console/home/__tests__/HomePage.approvalsTarget.test.tsx GET /api/v1/meta/_drafts usePendingDrafts.ts:48, a bare global fetch with no apiFetch seam installDraftsDouble() in a module-scope beforeEach
src/console/home/__tests__/HomePage.authoringCapabilityGate.test.tsx GET /api/v1/meta/_drafts usePendingDrafts.ts:48 installDraftsDouble() in a module-scope beforeEach
src/console/home/__tests__/HomePage.inboxLinksTarget.test.tsx GET /api/v1/meta/_drafts usePendingDrafts.ts:48 installDraftsDouble() in a module-scope beforeEach
src/console/home/__tests__/HomePage.notificationDeepLink.test.tsx GET /api/v1/meta/_drafts usePendingDrafts.ts:48 installDraftsDouble() in a module-scope beforeEach

Mechanism, measured rather than assumed. A trap-guarded stack probe injected
at the guard's own attribution point, restored by blob hash afterwards, attributed
every escape in all four files. Each render of HomePage mounts
PendingDraftsBanner, which reads the env-wide count through
usePendingDrafts({}); the hook's mount effect (usePendingDrafts.ts:116 via
refresh at :94) calls fetchPendingDrafts, which reaches the global fetch at
usePendingDrafts.ts:48. There is no apiFetch seam anywhere on that path — unlike
the apiFetch ?? fetch fallbacks batches 1 to 3 met, this hook only ever had the
global. Under happy-dom that global is a real HTTP client whose document URL is
http://localhost:3000, so the relative path resolved to a live socket, exactly
once per case (5 / 9 / 9 / 6 escapes against 5 / 9 / 9 / 6 tests). The read is
best-effort — its catch leaves count at null — which is why these files stayed
green while the request always failed.

Every escaped URL in all four files was http://localhost:3000/api/v1/meta/_drafts,
with no second endpoint anywhere. The probe was reverted with
git checkout HEAD -- path; the guard is byte-identical to HEAD afterwards
(blob back to 347ed78b, git diff HEAD empty, git status --porcelain empty).

The doubles are batch 1/2/3's shape verbatim: vi.stubGlobal('fetch', router)
in 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 afterEach fails on any URL outside the set it serves, so an escape elsewhere
reds here instead of vanishing into the hook's best-effort catch. Routes are
matched on the PATHNAME, because fetchPendingDrafts appends a ?packageId= scope
for package-bound callers; the full URL is what gets recorded.

What it answers, and why no assertion moves:

  • /api/v1/meta/_drafts — a known-EMPTY draft ledger, in the { drafts: [...] }
    envelope fetchPendingDrafts reads (the shape MetadataClient.listDrafts pins
    for this endpoint; the bare-array and { data: { drafts } } shapes parse to the
    same rows).
  • Empty rather than seeded is load-bearing here, and this is where the dispatch's
    warning about batches 1 to 3 was right not to transfer: the explain verdict those
    batches served was fail-open and unread, but a _drafts answer feeds visible UI
    state. PendingDraftsBanner renders null when (count ?? 0) is at most 0, and
    the failing request produced count === null, so an empty ledger yields
    byte-identical output to what these cases have always rendered. A seeded ledger
    would have added a banner paragraph and a pending-drafts-publish button to every
    case's tree — that would move what the files prove, not just what they fetch.
  • No case in any of the four asserts anything about pending drafts, and none of them
    depends on the request FAILING: each stubs useMetadataClient and
    usePublishAllDrafts at the module boundary already, and asserts only navigation
    targets, CTA enablement, or gate copy.

Nothing is skipped, quarantined or silenced.

Hook placement. All four install at MODULE scope rather than inside the existing
describe-level beforeEach. Two of the four (authoringCapabilityGate,
inboxLinksTarget) carry nested describe blocks with their own beforeEach, and
a module-scope pair is the one placement that provably covers every block in every
file; the ablation below runs against the file with the most nesting for exactly
that reason. The router's shape and the #7439 teardown ordering are unchanged either
way.

Ledger arithmetic

8 to 4, in both lists. The four 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 4, zero console/home rows left in
either. The pin's non-vacuity floor is length greater than 0, so 4 clears it.

Remaining 4, all app-shell, in three endpoint families: /api/v1/meta/object
(FlowNodeInspector.inactiveRetained, FlowNodeInspector.specKeys),
/api/v1/automation/_status (StudioDesignSurface.designerRegistryMissing),
/api/v1/ai/conversations (studioSurfaceContext).

No prose count moved. All five "21" references across the two ledger files are
provenance claims about the original sweep on 67dadd6, not live counts of the
current list — re-checked line by line, the same reading batch 3 recorded.

Evidence

Per file, before then after (attribution lines / ECONNREFUSED lines, then the
post-fix run on the final head):

file before after
HomePage.approvalsTarget 5 / 15 0 / 0, Tests 5 passed
HomePage.authoringCapabilityGate 9 / 19 0 / 0, Tests 9 passed
HomePage.inboxLinksTarget 9 / 27 0 / 0, Tests 9 passed
HomePage.notificationDeepLink 6 / 18 0 / 0, Tests 6 passed

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

pnpm exec vitest run packages/app-shell/src/console/home/ scripts/__tests__/network-escape-ledger.test.ts
on the final head 6a0c8d8dd: exit 0, Test Files 13 passed (13),
Tests 72 passed (72), and zero lines matching network-escape or
ECONNREFUSED in the whole run.

The whole app-shell package plus the pin also ran green (Test Files 633 passed (633), Tests 6080 passed | 1 skipped (6081), exit 0, 918s). That run is a superset,
not a zero-escape claim: its 18 remaining attribution lines all name the four rows
still on the ledger (FlowNodeInspector.specKeys 3, studioSurfaceContext 4,
FlowNodeInspector.inactiveRetained 1, StudioDesignSurface.designerRegistryMissing

  1. and zero of them name a console/home file. It was taken on the pre-merge
    commit d4558f65f; the merge that followed touched none of app-shell's sources,
    and the 13-file scope above was re-run on the merged head.

Ablation (on the committed tree, one script with trap ... EXIT INT TERM and
absolute paths throughout). HomePage.authoringCapabilityGate.test.tsx — the file
with the most describe nesting, so the one that most needs the module-scope
placement to be real — had its double reverted to its pre-batch-4 blob while its
line stayed deleted from BOTH ledgers. Mutation proven on disk, not by exit code:
blob 8c6b9dc7 to ddee802f, asserted equal to the 83a9d22a base blob and
unequal to HEAD's; anchors installDraftsDouble 2 to 0 and vi.stubGlobal 1 to 0;
that path counted 0 times in KNOWN_ESCAPES and 0 times in PINNED_LEDGER during
the run. Predicted direction stated before running: red naming the file.
OBSERVED red — exit 1, Test Files 1 failed (1), Tests 9 failed (9), with
Network escape: this test reached a REAL socket at http://localhost:3000/api/v1/meta/_drafts
and file: packages/app-shell/src/console/home/__tests__/HomePage.authoringCapabilityGate.test.tsx.
All nine cases failed, across all five describe blocks — which is the half that
shows the module-scope hooks really are what covers every block. Restore proven by
observation: blob back to 8c6b9dc7, git diff HEAD on that path empty,
git status --porcelain empty.

No dist preflight leg: these are the vitest projects' own test files and the root
config aliases every package specifier to src — measured, in that all four suites
ran, before and after, on a fully unbuilt tree (fresh install, no build had happened
yet).

Gates, exit codes captured by redirect-then-capture, never through a pipe, all
re-taken on the final head 6a0c8d8dd:

  • node scripts/check-changeset-presence.mjs exit 0 — "4 source file(s) of 1
    released package(s) changed, and this change declares 1 changeset(s)"; the
    changeset has an EMPTY frontmatter, the explicit exemption for a test-only change
    under a released package's src/, and not a label.
  • pnpm check:control-bytes exit 0 (6449 tracked text files), plus a grep -naP
    self-scan of the control range over every changed path, no hits.
  • node scripts/check-governed-queue-guard.mjs --test over all changed paths: exit
    0, "NOT GOVERNED — 15 path(s) checked against 5 governed surface(s)".
  • pnpm type-check:scripts exit 0 · pnpm check:vi-mock-inherit exit 0 (229 call
    sites judged on the widened specifier set test(console,app-shell,scripts): three 1-site specifiers join COVERED_SPECIFIERS (objectui#6892 slice 1) #8020 landed, 229 inherit, 0
    auto-mocked) · pnpm check:vi-mock-specifiers exit 0.
  • pnpm type-check:vitest-setup exit 0 — owed because this diff edits
    vitest.setup.network-escape-guard.ts, which lives in that program and no other.
    It is build-dependent by construction (vitest.setup.dom.tsx side-effect-imports
    four packages through their exports.types), so its first reading on the unbuilt
    tree was TS2882 on those four imports — a missing prerequisite, not a red gate, and
    it is green on the built tree. CI documents the same ordering at
    .github/workflows/ci.yml:623.
  • Per-package type-check and lint for app-shell via
    turbo --concurrency=2 --force: exit 0, Tasks: 32 successful, 32 total,
    0 eslint errors. Proven non-vacuous rather than assumed:
    tsc -p tsconfig.test.json --listFiles names each of the four edited files exactly
    once (in a 4421-file program), and eslint --format json lists each among the 1087
    linted files. Warning totals are unchanged (2894) and no warning is mine: each
    touched file's warnings sit on pre-existing lines strictly BEFORE this diff's
    insertion point, checked hunk range by hunk range (approvalsTarget warning at line
    74 vs hunks at 43, 45 and 122+; authoringCapabilityGate 63/83 vs 45, 47, 130+;
    inboxLinksTarget 73/85/86 vs 41, 43, 124+; notificationDeepLink 71/82 vs 41, 43,
    120+), and the inserted block contributes none.

The repo-wide eslint . --no-inline-config run belongs to CI, and CI is not awaited
here: the report is delivered at draft-PR time per the dispatch contract.
Live E2E (informational) is red on every branch today for the upstream reason
tracked in #7990 — not this PR's.

Concurrency

origin/main moved from 83a9d22a to 68912772 while this branch was being
verified (#8022, #8020, #8023 landed). It was merged in before this PR was opened —
read with git ls-remote and fetched into a private ref rather than moving the
shared remote-tracking name — and every reading above was re-taken on the merged
head 6a0c8d8dd
, including the one gate whose script the merge changed
(scripts/check-vi-mock-inherit.mjs, widened by #8020 from 226 to 229 judged call
sites; the four edited files pass under the widened rule). None of the three merged
commits touches console/home or either ledger.

Draft PR #7685 touches 17 app-shell paths, none under console/home/, and
FlowNodeInspector.specKeys — a batch 5 row deliberately left on the ledger here.
The domain:ui seat is active in packages/app-shell; this diff touches TEST files
under console/home/__tests__/ only, and no product source at all.

#7996 and #8014 are untouched and stay open: neither concerns this endpoint, and
neither double's shape was copied — this router answers the key the reader actually
reads.


Generated by Claude Code

…objectui#7307)

The four `console/home` HomePage tests each mounted `PendingDraftsBanner`, whose
`usePendingDrafts({})` fetches `GET /api/v1/meta/_drafts` with the global `fetch`
and no `apiFetch` seam. Under happy-dom that resolved to a real socket on
localhost:3000, once per case; the hook's best-effort `catch` is why the suites
stayed green while every request failed.

Each file now installs a recording router at module scope that answers
`/api/v1/meta/_drafts` with an empty draft ledger in the `{ drafts: [...] }`
envelope, 404s anything else, and asserts in `afterEach` that no URL outside the
served set was requested. `PendingDraftsBanner` renders `null` for both `count
=== null` (the failed read) and `count === 0` (an empty ledger), so no assertion
moves. `cleanup()` runs before `vi.unstubAllGlobals()` (objectui#7439).

Their four lines leave `KNOWN_ESCAPES` and `PINNED_LEDGER` in this same commit:
8 entries to 4 in both lists, verified by diffing the quoted paths of the two
literals against each other.

Part of #7307

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-BO0uFEv5.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.35KB 13.21KB
plugin-charts (index.js) 70.31KB 19.62KB
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.16KB 3.18KB
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 note — Live E2E (informational) is red on main and on every branch today: the upstream base-red anchored on #7990 (objectstack#16186). It is continue-on-error by design, never blocks the queue, and touches nothing this diff changes (four test files, the two escape ledgers, one empty changeset). Not this PR's to fix; no re-run requested. ACCEPTed on the card (#7307); flip and arming follow once the remaining checks conclude.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 11:59
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 78a9b74 Sep 6, 2026
33 of 34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7307-network-escapes-batch4 branch September 6, 2026 12:15
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