Summary
While chasing the lint CI failure on #786 we found that per-package yarn workspace <pkg> lint (which chains lint:types) had been passing locally only because stale incremental build state (tsconfig.tsbuildinfo and previously emitted .d.ts files) made tsc treat everything as up to date and skip re-checking. From a genuinely clean tree, lint:types fails with roughly 100 pre-existing errors:
@endo/ocapn: 78 errors, all in test/**
@endo/thixotrope: 22 errors, all in test/**
Reproduction
yarn clean
yarn workspace @endo/ocapn lint # 78 type errors
yarn workspace @endo/thixotrope lint # 22 type errors
Before yarn clean, both commands pass — same tree, same sources.
Why CI does not catch it
The CI lint job runs root prettier/eslint/shellcheck, yarn docs, root tsc -p tsconfig.json --noEmit, and yarn build:types. All of those are green (the root program and the composite build apply different settings and, for build:types, exclude test files). No CI job runs per-package lint:types, so the checked-in state drifted.
The dominant error pattern
Almost every error is the same shape: an eventual-send call on a presence typed as bare object, e.g.
test/hub.test.js(183,39): error TS2339: Property 'fetch' does not exist on type 'object'.
driven by presence-returning signatures such as Session.getBootstrap being declared () => object (packages/ocapn/src/client/types.js), so E(session.getBootstrap()).fetch(...) fails. Source files avoid it with per-call-site /** @type {any} */ casts (see client/sturdyrefs.js); the test files never got the casts.
Possible fixes
- Widen the handful of presence-returning signatures (
getBootstrap, getRemoteBootstrap, resolver/import providers) from object to any (or a dedicated RemotePresence alias). A remote presence's methods are only known to the caller, which invokes them through E(), so object provides no real checking today — it just forces casts. This fixes all ~100 errors in a few lines and lets the existing source-side casts be removed.
- Cast at each of the ~100 test call sites, keeping the narrow public types.
- Either way, consider adding per-package
lint:types (from clean state) to CI, since a gate that only passes with warm build state is not a gate.
Option 1 + 3 seems right; opinions welcome.
@kriscendobot could you review this diagnosis and weigh in on options 1–3?
Found during #786 (rebase onto llm); the head of that branch is green on the gates CI does run.
Summary
While chasing the
lintCI failure on #786 we found that per-packageyarn workspace <pkg> lint(which chainslint:types) had been passing locally only because stale incremental build state (tsconfig.tsbuildinfoand previously emitted.d.tsfiles) madetsctreat everything as up to date and skip re-checking. From a genuinely clean tree,lint:typesfails with roughly 100 pre-existing errors:@endo/ocapn: 78 errors, all intest/**@endo/thixotrope: 22 errors, all intest/**Reproduction
Before
yarn clean, both commands pass — same tree, same sources.Why CI does not catch it
The CI
lintjob runs root prettier/eslint/shellcheck,yarn docs, roottsc -p tsconfig.json --noEmit, andyarn build:types. All of those are green (the root program and the composite build apply different settings and, forbuild:types, exclude test files). No CI job runs per-packagelint:types, so the checked-in state drifted.The dominant error pattern
Almost every error is the same shape: an eventual-send call on a presence typed as bare
object, e.g.driven by presence-returning signatures such as
Session.getBootstrapbeing declared() => object(packages/ocapn/src/client/types.js), soE(session.getBootstrap()).fetch(...)fails. Source files avoid it with per-call-site/** @type {any} */casts (seeclient/sturdyrefs.js); the test files never got the casts.Possible fixes
getBootstrap,getRemoteBootstrap, resolver/import providers) fromobjecttoany(or a dedicatedRemotePresencealias). A remote presence's methods are only known to the caller, which invokes them throughE(), soobjectprovides no real checking today — it just forces casts. This fixes all ~100 errors in a few lines and lets the existing source-side casts be removed.lint:types(from clean state) to CI, since a gate that only passes with warm build state is not a gate.Option 1 + 3 seems right; opinions welcome.
@kriscendobot could you review this diagnosis and weigh in on options 1–3?
Found during #786 (rebase onto
llm); the head of that branch is green on the gates CI does run.