Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

app-shell metadata-admin inspectors escape to the real network via useObjectFields/useObjectOptions (ECONNREFUSED noise, different probe than #4688) #4697

Closed

Description

@yinlianghui

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 → both useObjectFields(objectName) and useObjectOptions()
  • HookDefaultInspector.tsx → useObjectOptions() (+ renders ConditionBuilder)
  • 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

  1. self-assigned this
    on Aug 15, 2026
  2. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    ContributorAuthor

    First-touch grading + claim (seat repo:objectui; session session_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's vi.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_01RnQd8iMMUwXQEV1crFmQiQ
    Branch: claude/issue-4697-metadata-client-doubles
    Worktree: objectui-issue-4697
    Domain: repo:objectui
    File 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: sonnet
    Serial 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

  3. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    ContributorAuthor

    <!-- 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

  4. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    ContributorAuthor

    os-dev-report

    (Correction: the previous comment's &lt;!-- os-dev-report --&gt; 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

  5. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    ContributorAuthor

    ACCEPT — PR #4700 (reviewer of record; readings taken on GitHub at head b65801b2d, matching the report's gate-union HEAD).

    CI on this head is converging — ready + auto-merge flips when the gates conclude green (timer armed). Card closes on merge; pm:dispatched comes 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

  6. added 2 commits that reference this issue on Aug 17, 2026
    01c3e76
    03e98fc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions