Skip to content

Fix Omit & Overrides intersection excess, dual-package extends, split indexed-access helpers - #315

Merged
kungfooman merged 6 commits into
mainfrom
fix-anchors-intersection-excess
Oct 29, 2026
Merged

kungfooman merged 6 commits into
mainfrom
fix-anchors-intersection-excess

Conversation

@kungfooman

@kungfooman kungfooman commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #314

What changed

  1. src-runtime/inspectType.js posts reports without a precomputed diagnosis: no per-error differ cost, no larger posts, no frozen rows.
  2. src-runtime/TypePanel.js buildCompareContent and src-runtime/compareText.js diff locally on open and on Copy, with comments stating the lazy contract (late twins can resolve more than the checker knew).
  3. src-runtime/reportedDiagnosis.spec.js (new, wired into test_runtime.js) pins the contract with seven honesty tests, each in same-realm and cross-realm (structuredClone) legs: reports carry no diagnosis; the lazy diff finds the true row from transported values; a registry-less panel degrades to one opaque row instead of inventing member rows; the anchors-shaped payload diffs to exactly one row; an option-drifted panel shows the extra strictness row; a validator-drifted panel reproduces the exact production phantom union row; and a typed-array diagnosis carries no $type row with same-vs-cross renders agreeing exactly.
  4. src-runtime/tagSnapshot.js no longer tags typed arrays and DataViews: an expando tag on a view does not survive structuredClone (typed-array serialization is buffer-only), so tagging printed a phantom informational $type row on the checking side and lost the tag at the realm boundary, while no consumer ever read view tags (snapshotTag rejects non-plain protos, snapshotMatches needs registered classes). Views now post untouched like Map/Set/Date. tagSnapshot.spec.js pins the new contract (testTypedArrayUntagged: identity, no tag).
  5. test/typechecking/anchors-diagnosis-input.mjs (+ -output.mjs, test/typechecking.json entry, anchors-diagnosis-errors.json) pins the worker verdict for the production shape: the arrays-plus-instance call passes, the alignment call throws exactly once (test/jsdoc-expect: 41/41 agree with all fixtures below).
  6. test/typechecking/playcanvas-tower-hostile-input.mjs (+ -output.mjs, test/typechecking.json entry, playcanvas-tower-hostile-errors.json) pins the worker verdict across every component family and hostile value: dense arrays into Vec | number[] unions, instances and tagged snapshots nominally, number | null both ways, frozen arrays, unknown names through the object false branch, dataless calls. Eleven throws pinned with values (sparse tail and typed array omit value: holes canonicalize ambiguously and views serialize as index objects). Three deliberate tsc divergences carry // Expected: notes (explicit null into optional-without-|null, frozen array into number[], sparse tail); every other line is tsc-strict clean with all @ts-expect-error directives consumed.
  7. src-runtime/validateArray.spec.js gains frozen/typed-array/extra-prop cases; src-runtime/componentTowerStress.spec.js gains explicit-null-into-optional, nullable-union members, union-all-fail and subclass-nominal cases: validator-level pins for what the hostile fixture exercises at worker level.
  8. Builtin utilities fixed and pinned: function nodes carry ret in all three expanders (expandType.js, expandTypeBabelTS.js, expandTypeDepFree.js, with triple parity holding), stringifyType prints it, Awaited unwraps to the fulfillment type instead of wrapper-checking (a Promise was wrongly accepted, results wrongly rejected), and ReturnType resolves declared returns. test/typechecking/builtin-utilities-input.mjs (+ -output.mjs, test/typechecking.json entry, builtin-utilities-errors.json) pins 14 throws with tsc-strict green on every @ts-expect-error; test/parity/cases/builtins.mjs pins 11 Awaited/ReturnType/intrinsic cases count-equal with tsc; validateReference.spec.js gains Awaited/ReturnType/intrinsic specs and stringifyType.spec.js gains the return-printing spec. The pre-existing substituteFunction ret handling needed no change.
  9. test/parity/cases/tower-hostile.mjs pins the tower against tsc per case (8/8 agree): dense arrays, instances, unknown-name fallthrough and typed arrays count-equal; frozen arrays, sparse tails and explicit null carry asserted-direction parity-diverges markers (quieter/quieter/stricter).
  10. test/parity/cases/builtins.mjs pins builtins against tsc per case (11/11 agree): Awaited unwrap, ReturnType resolution, all four intrinsics, union-distributed intrinsics, ReadonlyMap/ReadonlySet shapes, plus the documented Iterable shape-only divergence.
  11. test/typechecking/playcanvas-tower-read-input.mjs (+ -output.mjs, test/typechecking.json, playcanvas-tower-read-errors.json) pins the read path: findComponent/findComponents resolve through the return-position conditional, so lookups keep their type when the result flows into the next checked call, with unknown names falling back to Component. Three throws pinned with posted $type snapshots; one // Expected: note (live non-null instance vs declared | null — RTI checks values, tsc checks declarations).
  12. src-transpiler/Asserter.js no longer emits references to undeclared variables: a JSDoc @param naming nothing declared (including stray @overload blocks without a matching implementation signature) warns Missing param and skips instead of generating a ReferenceError at runtime. src-transpiler/generateTypeChecks.spec.js (new) pins the skip, the overload-docblock skip, the rest-binding exemption (...rest declares its binding) and the still-checking control.
  13. utilityNames in src-runtime/firstBadEntry.js completed to all 29 utility case branches of validateReference (was 20, missing ArrayLike family, ReadonlyArray/Map/Set, ConcatArray, async iterables). A registered class shadowing a missing name made the windowed map scan trust instanceof and return clean without running the loop — a false negative where the validator takes the utility branch. firstBadEntry.spec.js pins the collision (testUtilityNameCollisionBailsOut, mutation-verified: old set scans -1, new set bails -2).
  14. AGENTS.md gains the spec-source rule: JavaScript sources in specs are readable code in template literals, never \n-joined fragments or +-concatenated lines, with the snippet bound to its own const src, JSDoc stars aligned, and one-liners staying single-quoted (the Object.assign shape example documents the style). src-transpiler/generateTypeChecks.spec.js is converted to the rule: its five \n-joined sources become indented template literals. No other new spec needed conversion (the runtime specs build values or parse types directly, never JS sources).

Proof

  1. The production log (Entity#addComponent data, fontAsset -1, tagged Color, dense anchor/margin, [0,0] alignment) carries exactly three worker strings (Element data.alignment has wrong type., validateMapping failed), which is the current worker output for that payload, verified by replaying the log's own value/expect through the served bundle's explainMismatch (1 finding) and through the full comparator render path (1 Diagnosis row, green tree, single rti-fail-hit on alignment).
  2. The hostile drift hunt (scratch tmp/drift-hunt.mjs, 2691 same-vs-cross pairs over hostile arrays, typed arrays, Maps/Sets, functions, class instances, getters, null-proto objects, six option sets) reproduces the phantom-row class on demand: a hostile panel validateArray turns dense clean arrays into `[0,0,1,1]` at `data.anchor` matches no union member; closest is number[] with 1 problem, character-identical to the production rows. The committed drift specs pin this openly as panel behavior instead of suppressing it: the option-drift spec shows the strictness row, the validator-drift spec shows the phantom union row. After the view-untag fix the matrix shows 0 same-vs-cross divergences (34 before, all typed-array $type transit losses).
  3. The differ costs 0.45ms against 10.36ms validation on the production payload, but that ratio omits the two structuredClone passes and the larger posts, and it flips on cheap checks; the lazy path spends zero per error either way.
  4. Mutation check: re-adding eager findings breaks testReportCarriesNoDiagnosis; restoring the tagSnapshot.js view branch breaks the view-transit test with the phantom $type extra row; removing the local explainMismatch render calls breaks the foreign-registry spec (one opaque row).
  5. An earlier revision of this change posted checker-side findings with every report and rendered them verbatim; it was reverted after review: the eager path paid validation plus a full re-validation per distinct failure plus two extra structuredClone passes and larger posts (unmeasured in the 0.45ms-vs-10.36ms micro-benchmark, which also flips on cheap checks), and it froze the diagnosis at report time so late-loading twins could never add information. No code from that revision survives except its tests, rewritten as honesty specs.
  6. The tsc counter-check on the new fixture agrees with RTI on the verdict: the alignment call fails under tsc --strict (TS2739, array not assignable to Vec2) while the arrays-plus-instance call passes its options check.
  7. Every new throwing call carries @ts-expect-error (consumed in full runs) and every divergence from tsc carries a // Expected: note with the analysis; tsc-strict runs are green except for the documented lines: the builtin fixture (14 throws), the hostile tower (11 throws), the read tower (3 throws), and the parity suites (65/65 agree with asserted directions).

Latent holes

  1. The alignment failure itself is a genuine engine typing hole, deliberately left unfixed here: ElementComponent's getter declares @type {Vec2} with no ElementComponentOptionsOverrides entry, while the setter accepts Vec2|number[] and the example passes [0,0]. tsc reports the same split (TS2739 on the fixture's alignment call). Verdict: deferred to the engine repo (add an alignment?: Vec2 | number[] override next to anchor/margin/pivot, or widen the getter); RTI is correctly reporting what the declared options type says.
  2. buildTypeTree (the expandable type tree, not the Diagnosis rows) still diffs in the panel realm against the panel registry, so in a registry-less panel it marks value keys as informational extras. Verdict: tolerated deliberately (tree is exploratory navigation, Diagnosis is the verdict record; unifying the tree would mean posting the whole materialized tree per report).
  3. Posted findings are not length-capped: an object with thousands of bad keys posts thousands of leaves. Verdict: moot (no posted findings exist); the differ's own MAX_DEPTH/MAX_MAP_ENTRIES budgets bound the on-click diff, and repeat dedup prevents re-sends.
  4. Reports carry no diagnosis, so a stale checker plus a foreign panel disagree exactly as before this change. Verdict: deliberate (fail-closed and now documented by the drift specs); the validator-drift spec reproduces the production phantom rows on demand so the next reader starts from a pinned symptom instead of a cold hunt.
  5. firstBadEntry.utilityNames was incomplete (20 of 29 utility branches): a userland class shadowing ReadonlyMap, ArrayLike or Awaited made the windowed map scan return clean while validation failed — a silent false negative reachable with windowedChecks: true (which the engine opts into). No engine class collides today, so nothing ever misfired in production; the set is now complete and the collision spec pins the bail-out. Verdict: fixed here.

Dead code

  1. No branches were deleted: the local explainMismatch calls in buildCompareContent and compareText are the only render path, unchanged. Proof of life: the foreign-registry spec test deletes the registry and asserts the fallback path still renders (one opaque row), so removing either call would fail the suite.
  2. The scratch harnesses (tmp/fake-dom.mjs, tmp/render-compare.mjs, tmp/roundtrip.mjs, tmp/bundle-diagnosis.mjs, tmp/drift-hunt.mjs) stay out of the commit: the behaviors they probed are now pinned by reportedDiagnosis.spec.js (render path), tagSnapshot.spec.js (view transit), and anchors-diagnosis-errors.json (worker verdict).
  3. The removed ArrayBuffer.isView branch in tagName (src-runtime/tagSnapshot.js:38) can no longer produce a tag: the early return covers every view, so $type originates only from registered-class instances via registeredNameOf. Proof of death: testTypedArrayUntagged asserts identity return with no expando written, and the 2691-pair drift matrix shows zero same-vs-cross finding divergences involving views; re-adding the branch resurrects the phantom $type extra row the view-transit spec forbids.

@kungfooman kungfooman self-assigned this Oct 27, 2026
@kungfooman kungfooman added type assertions Everything related to type assertions. runtime labels Oct 27, 2026
Lazy on-click diagnosis with documented drift, tagSnapshot view fix,
Awaited/ReturnType/ReadonlyMap/ReadonlySet support, function ret
threading, mismatched-param crash fix, utilityNames completion,
plus anchors/hostile/read/builtin fixtures and parity cases.
Single-line \n-joined sources in generateTypeChecks.spec.js become
indented template literals bound to const src; AGENTS.md gains the
rule plus the Object.assign shapeFor example.
Parity fixtures lose their caseNN placeholders: every case function
is now named after what it checks (checkUppercase, addFrozenAnchor,
checkAwaitedPromise), and run.mjs matches plain lowercase names
anchored at line start instead of case digits. The twoArgs helper in
functions.mjs inlines as a named function expression with typed
params so no extra definition trips the runner.
All written JSON goes through JSON.stringify(value, null, 2) plus
trailing newline (31 expectation files reformatted, lock files
untouched); AGENTS.md records the rule, the meaningful-names rule,
and the token-economy rule (mechanical jobs belong in scripts, never
shuttle file contents through the model).
@kungfooman
kungfooman merged commit f0ec1b5 into main Oct 29, 2026
4 checks passed
@kungfooman
kungfooman deleted the fix-anchors-intersection-excess branch October 29, 2026 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

runtime type assertions Everything related to type assertions.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Element addComponent data wrongly rejected: anchor/margin arrays vs MergedComponentOptions<"element">

1 participant