Skip to content

test(unit): drive the runtime refusals from plain-JS specs instead of @ts-expect-error - #93

Merged
Upd4ting merged 1 commit into
mainfrom
test/untyped-refusal-specs
Nov 13, 2026
Merged

Upd4ting merged 1 commit into
mainfrom
test/untyped-refusal-specs

Conversation

@Upd4ting

Copy link
Copy Markdown
Member

Why

Antony wants every suppression guardrails.suppressions.json allows in this repo brought to zero by fixing the code it hides (ANT-1090). The 56 @ts-expect-error were all in unit specs that pass a value the types forbid on purpose, to check a runtime refusal. Those refusals exist for callers the type checker never sees: plain-JS modules and version skew. Antony chose to test them the way such a caller reaches them, from plain JavaScript. This PR does that and lowers tsExpectError from 56 to 0.

What

  • tsconfig.json sets allowJs: true (checkJs stays off) and adds src/**/*.js to include. The build compiles each *.untyped.spec.js into dist/test, where mocha and ajs module test already look.
  • Each of the 62 tests that carried a directive moves whole into a *.untyped.spec.js sibling of its TS spec. It keeps its describe and it titles and its assertions. One file per TS spec, 21 in all. pipeline-lambda-ids was all such tests, so it became JS whole.
  • Helpers both files need move out of the TS specs into fixtures: feeds-fixture.ts, selection-fixture.ts, pipeline-fold-fixture.ts and config-fixture.ts. They are moved, not rewritten.
  • The expression JS specs build their context from the shared expression-fixture or expression-array-fixture. The tests they came from used a file-local copy of the same context. The fields those tests read (age, name, createdAt) have the same types in both.
  • Type-aware oxlint also runs over the JS files. Two values keep their old TS type through a JSDoc annotation: REFUSED (readonly unknown[]) and one rejection callback parameter (unknown).

No production code changes.

flowchart LR
  subgraph before
    ts1["*.spec.ts"] -- "@ts-expect-error + bad value" --> guard1[runtime refusal]
  end
  subgraph after
    ts2["*.spec.ts"] --> typed[typed behaviour]
    js["*.untyped.spec.js<br/>(no type checking, like a JS caller)"] -- bad value --> guard2[runtime refusal]
    fx["*-fixture.ts"] --> ts2
    fx --> js
  end
Loading

SAFETY comments added

None.

Proof

pnpm run checks: Checks: 11 passed, 0 failed.

Same tests before and after. The unit suite, run as mocha JSON on base and head, compared by full test title and how often each title appears:

before 1356 passes 1356 | after 1356 passes 1356 failures 0 | titles differing: 0
untyped tests 62 in 21 files

The JS specs reach the refusals. The CROSS_INSTANCE refusal in NormalizeInstanceId is disabled on purpose, then restored:

# guard disabled
  2 passing (30ms)
  1 failing
  1) NormalizeInstanceId
       rejects CROSS_INSTANCE, naming the operation
# guard restored
  3 passing (10ms)

API: 1 packages, 1 entries unchanged against origin/main (committed HEAD).
API: skipped .agents/skills/verify-module, private.
Runtime API: 1 packages, 1 entries unchanged against origin/main (committed HEAD).
No behavioural probe ran.

Suppressions in authored code files (origin/main and committed HEAD). A lint directive counts once per rule it names; * is a directive naming none.

Suppressions base HEAD
Lint suppressions, all rules 1 1
Lint suppressions, anti-slop/no-unknown-parameters 1 1
@ts-expect-error 56 0
@ts-ignore 0 0
SAFETY: comments 0 0
Lint config rule-offs, all rules 0 0

@Upd4ting
Upd4ting merged commit f5013cf into main Nov 13, 2026
10 checks passed
@Upd4ting
Upd4ting deleted the test/untyped-refusal-specs branch November 13, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant