Repository navigation
app-shell metadata-admin inspectors escape to the real network via useObjectFields/useObjectOptions (ECONNREFUSED noise, different probe than #4688) #4697
Description
Split off objectui#4688 while implementing it. That card's own acceptance criteria anticipated this: "a remainder from a DIFFERENT probe is a new finding, not this card's" — measurement below shows the entire app-shell + plugin-designer 180-line ECONNREFUSED count objectui#4688 attributed to the grid's record-level explain probe is actually 100% a different, pre-existing probe, unrelated to ObjectGrid/useRecordCrudVerdicts/#4296.
Mechanism
packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts and useObjectOptions.ts both call useMetadataClient() (../useMetadata) with no override, and both fire their fetch unconditionally in a mount-time useEffect — no click, no host adapter required. useMetadataClient() returns createConsoleMetadataClient(...), which defaults its transport to createAuthenticatedFetch() (@object-ui/auth), which in turn calls the bare global fetch. Under happy-dom with no server listening, that is a REAL request to the default origin, exactly the same failure shape as objectui#3339 / objectui#4688 (connect ECONNREFUSED 127.0.0.1:3000) but a different endpoint (GET /api/v1/meta/object and GET /api/v1/meta/object/<name> — confirmed by instrumenting the fetch call site directly) — not POST /api/v1/security/explain.
Both hooks document graceful degradation on failure ("a missing object name, a 404, or a transport error all resolve to an empty list with error set"), so the failing calls do not fail any test — same "harmless noise" character as #4688's escape, just a different call site.
Five inspector components reach these hooks directly or via ConditionBuilder:
ConditionBuilder.tsx→useObjectFields(objectName)(no override)PageBlockInspector.tsx→ bothuseObjectFields(objectName)anduseObjectOptions()HookDefaultInspector.tsx→useObjectOptions()(+ rendersConditionBuilder)ActionDefaultInspector.tsx→useObjectFields(objectName)ViewVariantInspector.tsx→useObjectFields(...)
Measured (current main, b4bccc794)
Per-file ECONNREFUSED line counts, pnpm exec vitest run <file> --maxWorkers=1 each (isolated runs — a batched run undercounts because some in-flight rejections do not settle before the process exits):
| file | ECONNREFUSED lines |
|---|---|
packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.i18n.test.tsx |
70 |
packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.sectionName.test.tsx |
40 |
packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.celGate.test.tsx |
12 |
packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.condition.test.tsx |
12 |
packages/app-shell/src/views/metadata-admin/inspectors/ViewVariantInspector.celGate.test.tsx |
12 |
packages/app-shell/src/views/metadata-admin/inspectors/ViewVariantInspector.homeGate.test.tsx |
12 |
packages/app-shell/src/views/metadata-admin/inspectors/ActionDefaultInspector.celGate.test.tsx |
8 |
packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.test.tsx |
8 |
packages/app-shell/src/views/metadata-admin/inspectors/HookDefaultInspector.celGate.test.tsx |
6 |
Sum = 180, exactly matching objectui#4688's "app-shell + plugin-designer: 180" row when the full packages/app-shell/src/views/metadata-admin/inspectors/ directory is run together. Bisection confirmed no other directory in app-shell or in plugin-designer (which contributes 0) is involved: views/ top-level files → 0, views/studio-design/ → 0, views/metadata-admin/previews/ → 0, views/metadata-admin/inspectors/ → 180.
Diagnostic proof of the URL (instrumented globalThis.fetch via vi.stubGlobal inside vitest.setup.base.ts, output routed through process.stderr.write since console.error is swallowed by vitest for passing tests):
OS_DIAG_FETCH_URL /api/v1/meta/object
OS_DIAG_FETCH_URL /api/v1/meta/object/contact
Suggested direction
Same class of remedy as #3339/#4105/#4688 — a recorded double, not a global error sink — but for a different hook. packages/app-shell/src/views/metadata-admin/inspectors/FlowReferenceField.lookup.test.tsx already has the precedent shape for this exact package: vi.mock('../useMetadata', () => ({ useMetadataClient: () => stubClient })), where stubClient is a minimal { list: vi.fn(async () => []) , get: vi.fn(async () => null) } (or wire a real MetadataClient over a stub fetch, matching MetadataFieldsPage.retiredIndexed.test.tsx's pattern). Either keeps existing assertions passing unchanged, since none of the 9 files' assertions read the fetched object list/fields.
Provenance
Measured in worktree objectui-issue-4688 (branch claude/issue-4688-consumer-explain-doubles) while implementing objectui#4688, on top of b4bccc794.
Generated by Claude Code
Activity
yinlianghui commentedon Aug 15, 2026
First-touch grading + claim (seat
repo:objectui; sessionsession_01RnQd8iMMUwXQEV1crFmQiQ, 2026-08-15): promoted and dispatched in one stroke —pm:dispatched, size S, tests-only. The evidence is complete (per-file counts summing to the measured 180, URL proof at the fetch call site, bisection excluding every other directory) and the remedy has an in-package precedent (FlowReferenceField.lookup.test.tsx'svi.mock('../useMetadata')stub client shape). Acceptance = ECONNREFUSED 180 → 0 across the 9 named files with all assertions unchanged.Claim: PM loop round 11
Session:
session_01RnQd8iMMUwXQEV1crFmQiQBranch:
claude/issue-4697-metadata-client-doublesWorktree:
objectui-issue-4697Domain:
repo:objectuiFile surface: the 9 named test files under
packages/app-shell/src/views/metadata-admin/inspectors/(+ a shared local stub helper if the repo's in-package convention allows) + the tests-only empty-frontmatter changeset if the presence gate demands one. ⛔ No production source anywhere (stop on breach; explain in the report)Container & model: S,
mode:subagent,model: sonnetSerial constraints cleared: in-flight is only objectstack#8830 (other repo); PR #4698 (plugin-view half) is in CI on a disjoint package; no open dev PR touches these files (checked this round).
Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
<!-- os-dev-report -->
{ "issue": 4697, "status": "done", "branch": "claude/issue-4697-metadata-client-doubles", "pr": "https://github.com/objectstack-ai/objectui/pull/4700", "premise_still_valid": true, "summary": "Reproduced the card's exact baselines on current main (e05db8826): 70/40/12/12/12/12/8/8/6 ECONNREFUSED per file, sum 180. Stubbed useMetadataClient (../useMetadata) in each of the 9 named inspector test files with a per-file vi.hoisted()+vi.mock stable stub client ({get,list} both resolving empty), mirroring FlowReferenceField.lookup.test.tsx's precedent -- no shared helper file, since 9 short self-contained blocks reviewed more clearly than a new cross-file abstraction. Every file re-run isolated (maxWorkers=1) now shows 0 ECONNREFUSED with every existing assertion passing unchanged (same pass counts: 22,9,6,6,4,2,4,4,3 = 60 tests). Added a tests-only empty-frontmatter changeset (check-changeset-presence.mjs demanded one).", "tests": "Baselines on main (e05db8826), each file isolated via `pnpm exec vitest run` with maxWorkers=1: PageBlockInspector.i18n=70, .sectionName=40, ConditionBuilder.celGate=12, HookDefaultInspector.condition=12, ViewVariantInspector.celGate=12, .homeGate=12, ActionDefaultInspector.celGate=8, ConditionBuilder.test=8, HookDefaultInspector.celGate=6 (sum 180, matches the card). After the fix, same isolated commands: all 9 files 0 ECONNREFUSED, all previously-passing assertions still pass (22/22, 9/9, 6/6, 6/6, 4/4, 2/2, 4/4, 4/4, 3/3). Directory suites: the inspectors/ directory -- 53 files, 590 passed plus 1 skipped (pre-existing, unrelated), 0 ECONNREFUSED. Full metadata-admin/ directory -- 167 files, 1671 passed plus 1 skipped, 0 ECONNREFUSED (a separate ECONNRESET noise from previews/FlowCanvas.test.tsx, unrelated mechanism, filed as #4699). Reverse verification (direction predicted first: revert then count returns to baseline, tests stay green): committed the fix (b65801b2d), reverted PageBlockInspector.i18n.test.tsx to the pre-fix blob via git checkout, re-ran isolated -- 70 ECONNREFUSED, 22/22 still passing (confirmed prediction); restored the file from the fix commit -- back to 0 ECONNREFUSED, 22/22 passing. Gate union at HEAD b65801b2d (quoted from the post-commit run): dependency closure build (pnpm --filter with the app-shell-dependents pattern) green; app-shell's type-check script (tsc --noEmit plus tsc -p tsconfig.test.json) green; eslint --quiet on the 9 changed files clean, no output; node scripts/check-changeset-presence.mjs green (1 empty-frontmatter changeset declared); pnpm run check:control-bytes green (4223 files scanned). scripts/pm/dispatch-gates.mjs does not exist in this repo (objectui, unlike objectstack) so I hand-derived implicated gate families against the actual diff (9 test files plus 1 changeset): no new error codes, no .claude/agents changes, no new fake engine, no t() call-site changes, no doc changes -- no additional family beyond the ones named/run above.", "open_questions": [], "out_of_scope_findings": [ "filed as #4699: app-shell previews/FlowCanvas.test.tsx escapes to the real network (ECONNRESET / socket-hang-up noise, a different transport mechanism than #4688's/#4697's plain-fetch ECONNREFUSED) -- 19 lines isolated, harmless (all assertions pass), found while re-running the full metadata-admin/ suite for this card's own step-2 verification; unassigned, not touched here" ] }Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
os-dev-report
(Correction: the previous comment's
<!-- os-dev-report -->marker survived only as escaped entities, not as a literal match. This comment is the report of record.){ "issue": 4697, "status": "done", "branch": "claude/issue-4697-metadata-client-doubles", "pr": "https://github.com/objectstack-ai/objectui/pull/4700", "premise_still_valid": true, "summary": "Reproduced the card's exact baselines on current main (e05db8826): 70/40/12/12/12/12/8/8/6 ECONNREFUSED per file, sum 180. Stubbed useMetadataClient (../useMetadata) in each of the 9 named inspector test files with a per-file vi.hoisted()+vi.mock stable stub client ({get,list} both resolving empty), mirroring FlowReferenceField.lookup.test.tsx's precedent -- no shared helper file, since 9 short self-contained blocks reviewed more clearly than a new cross-file abstraction. Every file re-run isolated (maxWorkers=1) now shows 0 ECONNREFUSED with every existing assertion passing unchanged (same pass counts: 22,9,6,6,4,2,4,4,3 = 60 tests). Added a tests-only empty-frontmatter changeset (check-changeset-presence.mjs demanded one).", "tests": "Baselines on main (e05db8826), each file isolated via `pnpm exec vitest run` with maxWorkers=1: PageBlockInspector.i18n=70, .sectionName=40, ConditionBuilder.celGate=12, HookDefaultInspector.condition=12, ViewVariantInspector.celGate=12, .homeGate=12, ActionDefaultInspector.celGate=8, ConditionBuilder.test=8, HookDefaultInspector.celGate=6 (sum 180, matches the card). After the fix, same isolated commands: all 9 files 0 ECONNREFUSED, all previously-passing assertions still pass (22/22, 9/9, 6/6, 6/6, 4/4, 2/2, 4/4, 4/4, 3/3). Directory suites: the inspectors/ directory -- 53 files, 590 passed plus 1 skipped (pre-existing, unrelated), 0 ECONNREFUSED. Full metadata-admin/ directory -- 167 files, 1671 passed plus 1 skipped, 0 ECONNREFUSED (a separate ECONNRESET noise from previews/FlowCanvas.test.tsx, unrelated mechanism, filed as #4699). Reverse verification (direction predicted first: revert then count returns to baseline, tests stay green): committed the fix (b65801b2d), reverted PageBlockInspector.i18n.test.tsx to the pre-fix blob via git checkout, re-ran isolated -- 70 ECONNREFUSED, 22/22 still passing (confirmed prediction); restored the file from the fix commit -- back to 0 ECONNREFUSED, 22/22 passing. Gate union at HEAD b65801b2d (quoted from the post-commit run): dependency closure build (pnpm --filter with the app-shell-dependents pattern) green; app-shell's type-check script (tsc --noEmit plus tsc -p tsconfig.test.json) green; eslint --quiet on the 9 changed files clean, no output; node scripts/check-changeset-presence.mjs green (1 empty-frontmatter changeset declared); pnpm run check:control-bytes green (4223 files scanned). scripts/pm/dispatch-gates.mjs does not exist in this repo (objectui, unlike objectstack) so I hand-derived implicated gate families against the actual diff (9 test files plus 1 changeset): no new error codes, no .claude/agents changes, no new fake engine, no t() call-site changes, no doc changes -- no additional family beyond the ones named/run above.", "open_questions": [], "out_of_scope_findings": [ "filed as #4699: app-shell previews/FlowCanvas.test.tsx escapes to the real network (ECONNRESET / socket-hang-up noise, a different transport mechanism than #4688's/#4697's plain-fetch ECONNREFUSED) -- 19 lines isolated, harmless (all assertions pass), found while re-running the full metadata-admin/ suite for this card's own step-2 verification; unassigned, not touched here" ] }Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
ACCEPT — PR #4700 (reviewer of record; readings taken on GitHub at head
b65801b2d, matching the report's gate-union HEAD).main, first lineFixes #4697— full deliverable; app-shell previews/FlowCanvas.test.tsx escapes to the real network (ECONNRESET/"socket hang up" noise, different mechanism than #4688/#4697) #4699 referenced bare. Scope (10 files): the 9 named test files + the tests-only empty-frontmatter changeset the presence gate demanded — no production source, exactly the boundary.[client, …], so a fresh object per render would re-fire them) is the detail that separates a correct double from a subtly-flaky one; the no-shared-helper call is fine at this size.ECONNRESET— a different transport mechanism, 19 lines) — correctly filed, not folded; graded on its own card.CI on this head is converging — ready + auto-merge flips when the gates conclude green (timer armed). Card closes on merge;
pm:dispatchedcomes off then. With this and PRs #4689/#4698, the entire measured ECONNREFUSED noise across the four packages is attributed and closed.Generated by Claude Code
test(app-shell): stub useMetadataClient in metadata-admin inspector s…
test(app-shell): stub the automation/actions fetch in FlowCanvas.test…