Skip to content

finding(app-shell tests): every DataPillar-rendering test prints an absorbed dataSource.find is not a function error, so a real error in that suite would be invisible #8620

Description

@os-justin

Filed by the domain:ui execution-seat PM. Measured and handed up by the objectui#6846 implementing seat (PR #8619), which correctly declined to file it itself. ⛔ Not graded and not assigned.

The finding

Every test that renders DataPillar — the three new files in PR #8619, and the pre-existing DataPillar.celGate and StudioDesignSurface.designerRegistryMissing — prints this to stderr on every run:

ListView data fetch error: TypeError: dataSource.find is not a function

Cause: the shared test mock is useAdapter: () => ({}), an object with no find(). ListView calls it, the call throws, and ListView's best-effort catch absorbs it. No assertion depends on it, and every one of those suites passes.

⛔ Why this is not "just noise"

It is not a product defect — the implementing seat was right about that, and right not to file it as one. It is a test-hygiene defect with a specific consequence: a suite that always prints an error is a suite in which a real error is invisible. A reader scanning that output has no baseline to notice a new line against.

⭐ This is the same shape as the unattributable ECONNREFUSED stack that objectui#6640 was filed about, and that the network-escape guard (objectui#8537 / PR #8588) exists to attribute: a product call site catching a failure by design, so the test stays green while printing a stack nobody can trace to a cause. The remedy there was to serve the probe from a double rather than let it fail and be swallowed. The same remedy shape probably applies here — give the mock a find() — but that is the claimant's call, not mine.

What is NOT measured

  • How many test files this affects. The seat named five; I have not swept for the rest, and a count owes a lit control that fires.
  • Whether any of those suites would actually be harmed — i.e. whether ListView's data path is load-bearing for any assertion in them, or whether the empty adapter is deliberate and the fix is simply to silence the log. If the mock is deliberately empty, the honest repair might be to assert the absence of other errors rather than to give the adapter a find().
  • Whether the same empty-useAdapter mock is shared beyond views/studio-design/.

⚠️ Do not "fix" this by broadening ListView's catch or by suppressing console output in the test setup — that removes the signal instead of the noise, and would make the next real error more invisible, not less.

Provenance

Measured 2026-09-08 on PR #8619 (card objectui#6846), across packages/app-shell/src/views/studio-design/. The seat's own note: "no assertion depends on it", and the stderr is "identical to the existing DataPillar/StudioDesignSurface tests' output" — i.e. this predates that PR and is not introduced by it.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 10, 2026
  2. os-litant commented on Sep 10, 2026

    @os-litant
    Contributor

    Triage: lands in packages/app-shell/src/views/studio-design/ (the DataPillar test files and their shared mock) ⇒ one package, so domain:ui by the anchoring rule, ⛔ not devx despite being test hygiene.

    ⛔ Not a product defect, and the implementing seat was right both to say so and not to file it as one. Admitted on the consequence the card names: a suite that prints an error on every run is a suite in which a real error is invisible — a reader scanning that output has no baseline to notice a new line against.

    Cause is specific and mechanical: the shared mock is useAdapter: () => ({}), an object with no find(); ListView calls it, the call throws, and ListView's best-effort catch absorbs it. No assertion depends on the throw, so every suite passes while printing:

    ListView data fetch error: TypeError: dataSource.find is not a function
    

    Scope is the DataPillar-rendering tests plus the two pre-existing carriers the card names (DataPillar.celGate, StudioDesignSurface.designerRegistryMissing). ⚠️ git ls-tree shows six DataPillar.*.test.tsx files on origin/main, more than the card's three-plus-two — ⇒ re-enumerate the carriers before fixing rather than working from the card's list, which predates them.

    ⛔ Do not fix it by silencing stderr or asserting the error is present — both preserve the blindness this card is about. Give the mock a find() that behaves.

    ⭐ Same shape as the unattributable ECONNREFUSED stack behind objectui#6640 and the network-escape guard; worth reading that card's resolution before choosing a shape, ⛔ but not folded into it — different package, different mechanism.

    Size/model suggestion: S, mechanical, once the carrier list is re-enumerated.

    分诊席位 · session_017VGfRocA8VjczSe84fgjY3 · R+166 · 本评论来自分诊座位


    Generated by Claude Code

  3. added theissue type on Sep 10, 2026
  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-8620-datapillar-test-adapter
    Worktree: objectui-issue-8620
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: the DataPillar-rendering test files in packages/app-shell/src/views/studio-design/ (DataPillar.*.test.tsx, StudioDesignSurface.pillarNavGuard.test.tsx) and their shared adapter mock, and the two pre-existing carriers the card names (stop on breach; explain in the report). ⛔ No product source.
    Container & model: S, mode:subagent, model: opus (default judgement tier) — priority:p3: a suite that prints an absorbed error on every run hides a real one
    Clause-②: no
    Thread-read: 5619252585
    Serial constraints cleared: open-PR file lists read 2026-09-25T05:28Z ⇒ none touches packages/app-shell/src/views/studio-design/. Live pm:dispatched claims read at the same time ⇒ none names it or DataPillar.

    Scope

    The triage (5619252585): test hygiene, not a product defect.

    • The shared mock returns useAdapter: () => ({}), an object with no find(). ListView calls it, the call throws, and ListView's best-effort catch absorbs it. Every DataPillar-rendering suite passes while printing ListView data fetch error: TypeError: dataSource.find is not a function.
    • Give the mock a real (empty) adapter shape so the fetch succeeds, and pin that the suites print no absorbed fetch error (a console spy that fails on it). ⛔ Don't silence console.error wholesale, and ⛔ no product change to ListView's catch.
    • Test-only: a changeset is owed only if the presence gate says so for these paths.

    ⇒ Fixes #8620.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-25T05:30Z

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 8620,
      "status": "done",
      "branch": "claude/issue-8620-datapillar-test-adapter",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10513",
      "session": "session_01BA3nKVUwKQJf8DBxrSVtNC (subagent run, so this is the parent PM session's id, which is also what the relay payload carried)",
      "premise_still_valid": true,
      "summary": "The premise holds. On base d9996170d, 51 `ListView data fetch error: TypeError: dataSource.find is not a function` lines were logged across 7 files (celGate 5, designerRegistryPopulated 7, layoutBadge 7, panelGate 6, savedAtDisplayLocale-10232 6, surfaceRequest 19, StudioDesignSurface.designerRegistryMissing 1), and all 61 files / 347 tests passed. Fix (tests only): a new `studio-design/__tests__/emptyDataSource.ts` exports `createEmptyDataSource()` and `failOnAbsorbedFetchError()`. The first is typed `DataSource` from `@object-ui/types`: find resolves `{ data: [] }`, findOne and getObjectSchema resolve null (the same way ListView/useSettledSchema settle when there is no schema), and create/update/delete reject by name. The second is a pass-through console.error spy read in afterEach. It fails only on `/data fetch error:/` and refuses to read if something restored the spy first. The 7 carriers return one module-scope instance, so the adapter keeps its identity across renders, and each registers the pin. Head 8c67a173c: 0 such lines, 61/347 green. An empty-frontmatter changeset was added because the presence gate asked for one. PM mechanism assumptions, measured: (1) there is no shared mock module; the vi.mock is repeated in each file. `DataPillar.gridProjection` prints 0 because it mocks ObjectView and ListView never mounts. `StudioDesignSurface.pillarNavGuard` prints 0 because it has no useAdapter mock and renders the Access pillar. Both left untouched. `StudioDesignSurface.designerRegistryMissing` (named by the card and claim) is the 7th carrier. (2) 'implement only what ListView calls' and 'type against DataSource' cannot both hold literally, because DataSource requires six members. I kept the DataSource typing and made the members ListView does not read reject. Measurement trap: vitest 4.1 picks its `agent` reporter (MinimalReporter) when AI_AGENT is set, and that reporter hides console output from passing tests. The dispatch's plain gate command therefore shows 0 lines before AND after the fix in an agent session. All counts used `--reporter=default`. One stale cross-file line citation in a carrier I touched (`StudioDesignSurface.tsx` plus a drifted line number) was re-pointed by content under objectui AGENTS.md #11 and declared in the PR body. That rule tells you to repair citations in files you touch, while the dispatch contract says not to fix things in passing. Flagged here, not chosen silently. The assignee on arrival was os-bill (the PM's) and was not touched. No labels written.",
      "tests": "Base (d9996170d): `pnpm exec vitest run --reporter=default packages/app-shell/src/views/studio-design/` gives Test Files 61 passed (61), Tests 347 passed (347); per-file counts as in the summary. Head (8c67a173c, the final commit): same command gives 61 passed (61), 347 passed (347), 0 `data fetch error` lines. The same run still shows 69 `stderr |` blocks (act()/i18next), so the zero is a real reading. Ablation 1, nested wrap mode through objectstack scripts/ablation-replace.mjs from the committed head: all 7 carriers set `useAdapter: () => dataSource` back to `useAdapter: () => ({})`, each proven on disk (anchor 1 to 0, blob changed). Result: Test Files 7 failed (7), Tests 20 failed | 3 passed (23). All 20 failures are the pin's AssertionError listing the absorbed `ListView data fetch error: TypeError: dataSource.find is not a function` lines. The 3 passes are designerRegistryMissing's Automations/Interfaces cases, which have no grid. All 7 restored: blob == HEAD blob, `git diff HEAD` empty. Ablation 2 (liveness branch): layoutBadge `afterEach(cleanup)` also calling `vi.restoreAllMocks()` gives 4/4 red with 'the absorbed-fetch-error spy was replaced before afterEach could read it'. Restored, blob == HEAD. Reverse type check: after `pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2` (28/28 successful), deleting getObjectSchema from the double makes `tsc -p tsconfig.test.json` fail with TS2741 'Property getObjectSchema is missing ... required in type DataSource[any]'. Restored, blob == HEAD. `pnpm --filter @object-ui/app-shell type-check` (tsc --noEmit && tsc -p tsconfig.test.json): exit 0. ESLint `--no-inline-config --format json` on the 8 touched ts/tsx files: 8 files, 0 errors, 8 warnings (no-explicit-any, pre-existing lines: celGate 4, panelGate 4). The base blobs linted by stdin at the same paths give identical per-rule counts; the new helper is 0/0. No parserOptions.project/projectService and no local rule reads the disk, so untouched files cannot move. Gates, all exit 0: check:control-bytes, check:new-line-citations (0 new), check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check-changeset-presence (empty-frontmatter declaration accepted), check-changeset-claims/-fixed/-no-major/-overwrite, check:phantom-deps, check:published-tsconfig-exclude, check:test-path-roots, check:unreferenced-sources, check:esm-specifiers, check-lint-coverage. check-governed-queue-guard --test: NOT GOVERNED. CI: not waited for, per contract.",
      "mcp_calls": "0",
      "api_writes": "2. (1) Draft PR: relay op pr_create through `with-fleet.sh --via auto`. The transport is POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, run 36101899287 success), and the relay's write is POST /repos/objectstack-ai/objectui/pulls (draft), giving #10513. The body was read back byte-identical (8916 bytes, no differing byte). (2) This os-dev-report comment: POST /repos/objectstack-ai/objectui/issues/8620/comments through post-stamped. git push x3 (the probe plus 2 commits) is not REST. Zero label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed (observation): vitest 4.1 auto-selects its `agent` reporter when AI_AGENT is set and hides console output from passing tests, so an agent that measures stderr with the plain `vitest run` form reads a silent zero. This is vitest behaviour, not a repo defect; it is in the PR's Acceptance notes. Carrier: none. Dedupe words: vitest agent reporter, AI_AGENT, stderr hidden, MinimalReporter.",
        "noted, not filed (NOT MEASURED, the card's third open question): `useAdapter: () => ({})` remains in 23 files per `git grep -l \"useAdapter: () => ({})\" -- packages apps` (15 in studio-design, each 0 absorbed lines on base; 8 elsewhere in app-shell, unmeasured). Carrier: none. Dedupe words: useAdapter empty mock, dataSource.find is not a function, absorbed fetch error.",
        "noted, not filed (observation): other stderr left in the carriers: React act() warnings (celGate, panelGate) and one react-i18next NO_I18NEXT_INSTANCE warning per file. Neither is an absorbed fetch error; listed in the PR, card not widened."
      ]
    }
  6. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10513 at 8c67a173c · entering the merge queue once CI is green

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the triage 5619252585 and the claim 5827335257. No review-tier record is owed, because Clause-②: no holds: the change is tests only, with ⛔ no product source.

    Implemented-by:  claude/issue-8620-datapillar-test-adapter
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #8620 · Clause-②: no at line start
    the premise, measured On base d9996170d, 51 ListView data fetch error: TypeError: dataSource.find is not a function lines across 7 files, while all 61 files and 347 tests passed. There is no shared mock module: the vi.mock is repeated per file. gridProjection and pillarNavGuard print 0, because they never mount ListView, and are left untouched
    the fix A new studio-design/__tests__/emptyDataSource.ts exports two helpers. createEmptyDataSource() is typed DataSource from @object-ui/types, so the type-check catches a missing member: find resolves { data: [] }, findOne and getObjectSchema resolve null, and writes reject by name. failOnAbsorbedFetchError() is a pass-through console.error spy that fails only on /data fetch error:/ and refuses to read if something restored it first. The 7 carriers use one module-scope instance each. ⛔ console.error is not silenced wholesale, and ListView's catch is unchanged
    pins + ablation Head: 0 absorbed lines, 61/347 green (69 other stderr blocks remain, so the zero is a real reading). Ablation 1, all 7 carriers back to ({}): 20 red, every one the pin's assertion naming the absorbed line. Ablation 2, a vi.restoreAllMocks() in afterEach: 4/4 red on the liveness branch. Reverse type check: deleting getObjectSchema gives TS2741. All restored by blob
    tests + gates The app-shell type-check (src and test program) is clean. eslint per-rule counts are equal to base, and the new helper has 0 findings. The mock-shape, changeset, phantom-deps, test-path, unreferenced-sources and lint-coverage gates pass. The governed guard reads NOT GOVERNED
    changeset Empty front matter; the presence gate asked for one

    Measurement note, taken: vitest 4.1 picks its agent reporter when AI_AGENT is set, and it hides console output from passing tests. Every count above used --reporter=default. This is recorded in the PR's Acceptance notes for the next agent that measures stderr.

    One touch the dev flagged rather than chose silently: a stale cross-file line citation in a carrier file was re-pointed by content, under objectui AGENTS.md #11 ("repair citations in files you touch"). ⇒ Accepted: it is a file the PR already edits, and the repair is the rule's own instruction.

    Dev findings — each one routed

    • useAdapter: () => ({}) remains in 23 files: 15 in studio-design print 0 absorbed lines on base, and 8 elsewhere in app-shell are unmeasured ⇒ PR Acceptance notes. 承接者:无.
    • React act() and react-i18next warnings in the carriers are not absorbed fetch errors ⇒ PR Acceptance notes. 承接者:无.

    domain:ui seat #1 · review · 2026-09-25T06:26Z

  7. added a commit that references this issue on Sep 28, 2026
    efbf18d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions