Skip to content

ci: type-check is excluded from examples/** and testing/** — call-site type regressions slip through test:pr #820

Description

@tombeckenham

Context

test:pr (the canonical pre-PR gate, mirrored by the PR CI workflow) runs:

nx affected --targets=test:sherif,test:knip,test:docs,test:eslint,test:lib,test:types,test:build,build --exclude=examples/**,testing/**

The --exclude=examples/**,testing/** carve-out means test:types never typechecks the example apps or the testing/ packages (testing/e2e, testing/panel). This leaves a real hole: a type regression that only manifests at a call site is invisible to CI when the only call sites live in the excluded folders.

Motivating repro (real bug, found by hand during a rebase, not by CI)

grokSummarize(...) is not assignable to summarize()'s adapter param for any current Grok model (grok-4.3, grok-build-0.1):

Type 'ChatStreamSummarizeAdapter<"grok-4.3", GrokTextProviderOptions>' is not assignable to
  'SummarizeAdapter<string, object>'.
  Types of property 'summarize' are incompatible.
    ... Type 'SummarizationOptions<object>' is not assignable to 'SummarizationOptions<GrokTextProviderOptions>'.
      Type 'object' is not assignable to type 'GrokTextProviderOptions'.

Root cause: SummarizeAdapter.summarize is declared as a property (arrow type), so under strictFunctionTypes its parameter is checked contravariantly. Assignability to the constraint SummarizeAdapter<string, object> therefore reduces to "is object assignable to TProviderOptions?". GrokTextProviderOptions extends Record<string, unknown> (it carries an index signature), and object is not assignable to Record<string, unknown> — so it fails. OpenAI's options are all-optional with no index signature, so object is assignable and it passes. (Separate fix tracked for the Grok options shape; this issue is about the CI gap.)

Why test:pr doesn't catch it

Two factors compound:

  1. The error only exists at a summarize({ adapter }) call site. The constraint TAdapter extends SummarizeAdapter<string, object> is only instantiated when summarize() is actually called with a concrete adapter. Merely constructing the adapter typechecks fine — which is all packages/ai-grok/tests/grok-adapter.test.ts does (asserts .kind, never calls summarize()). So the ai-grok package's own test:types stays green.
  2. The only real call sites are in excluded folders. testing/e2e/src/routes/api.summarize.ts and testing/panel/src/routes/api.summarize.ts pass a Grok summarize adapter into summarize() — but testing/** is excluded from test:types. (testing/panel also casts grokSummarize(model as any), which would mask it there regardless.)

Net result: nothing in the affected + included set ever instantiates the constraint, so the regression sails through CI.

Proposal

Extend type-checking to the currently-excluded surfaces so call-site regressions are caught:

  • Run test:types over examples/** and testing/** in CI (either drop them from the --exclude for the test:types target specifically, or add a dedicated test:types:integration target/job that covers them). Examples aren't built by Nx today, so this likely needs project-level test:types wiring for the example apps.
  • Keep the heavy targets (test:build, build, test:lib) excluded for examples if desired — the gap here is specifically types, which are cheap to check and high-value for a library that sells type safety.
  • Add a minimal call-site type assertion inside an included package as a fast guard for the common case, e.g. in packages/ai-grok/tests/:
    // @ts-expect-error or a positive assertion once the Grok options shape is fixed
    summarize({ adapter: grokSummarize('grok-4.3'), text: '' })
    This catches the per-provider adapter→activity contract without waiting on full example coverage.

Acceptance

  • CI fails if an example or testing/ package has a type error.
  • The Grok summarize call-site regression above is caught by CI (via example/testing coverage and/or the in-package call-site assertion).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions