Skip to content

[finding] app-showcase's job sweep still normalizes { records } off an engine find() the contract types as an array #14460

Description

@os-musk

Found while implementing #14257 (PR #14459), which is where the handler first became typed against the real engine contract. Out of that card's scope — a different defect class — so it is recorded rather than fixed there.

Measured

examples/app-showcase/src/automation/jobs/sweep-project-health.ts carries:

/** Normalize the engine's list shape (array, or `{ records }`). */
function rowsOf(result: unknown): Array... {
  if (Array.isArray(result)) return result as ...;
  const records = (result as { records?: unknown })?.records;
  return Array.isArray(records) ? (records as ...) : [];
}

Both of its read sites now pass await ql.find(...) where ql is IObjectQLEngine off JobHandlerContext. IDataEngine.find is declared

find(objectName: string, query?: EngineQueryOptions, options?: BaseEngineOptions): Promise...any[];

so the { records } limb is unreachable through the declared contract, and rowsOf is an identity on everything the engine can hand it. Before #14257 the handler's engine was a locally-declared JobHostEngine returning Promise of unknown, which is why the tolerance read as reasonable there and does not any more.

Why it is worth recording

This is the tolerant-consumer shape Prime Directive #12 rules out and that this same app has had deleted twice already — the ?? records aliases (#13705) and the unreachable Array.isArray limb beside them (#13706 / #13969), both at adapter.find() sites in src/ui/pages/. pnpm check:react-page-adapter-contract gates that class, but its population is *.page.ts / *.pages.ts plus the react-pages doc, so an automation job under src/automation/ is outside it and nothing reds today.

It also matters more here than the line count suggests: this is the repo's only shipped defineJob, so it is what an author copies when they write scheduled work.

Two things a fix has to decide, hence a finding and not a patch

  1. Whether the engine find direction really is the mirror image of the adapter one. [finding] The Array.isArray(result) limb beside the retired ?? records alias is also unreachable — normalizeQueryResult() wraps a bare array, so find() never resolves to one #13706 concluded the OPPOSITE for the react-page adapter — normalizeQueryResult() wraps a bare array, so that find() never resolves to one. The two finds are different functions; the engine's declared return should be confirmed against ObjectQL's implementation, not inferred from the sibling card.
  2. Whether the fix is local (delete rowsOf, read the array) or a gate widening, i.e. whether check:react-page-adapter-contract's no-array-ness detector should grow a population beyond page modules. The second is the one that closes the class rather than one instance.

Unassigned and untriaged.

Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions