ReviewOS

home-lang/home

fix(ts): preserve Promise chain and all tuple result types

#475
Open chris opened this 8 days ago ·
8 days ago

Follow-up to #473 and #416. The pre-admission async audit uses identical strict/noEmit/noLib/skipLibCheck/ES2022 inputs and unchanged Promise, PromiseLike, Awaited and PromiseConstructor declarations extracted from TypeScript 6.0.3. Pinned TS 6.0.3 and native TS 7.0.2 reject both independently isolated controls with TS2322; Home silently accepts both:

declare const pending: Promise<{ label: string }>;
async function chained() {
  const result = await pending.then(value => value.label);
  const good: string = result;
  const bad: number = result;
}
declare const numeric: Promise<number>;
declare const text: PromiseLike<string>;
async function combined() {
  const result = await Promise.all([numeric, text] as const);
  const good: number = result[0];
  const bad: string = result[0];
}

Source audit: synthesized structural Promise members and global Promise statics currently have permissive any returns. Fix type propagation through real shared declarations/general generic signatures; do not implement benchmark-specific spelling or source shortcuts. Include plain results, PromiseLike assimilation, rejection branches/defaults, readonly tuple positions, negative controls and declaration shadowing as appropriate. Async remains excluded from timing until all controls pass. Separate from primitive function-return enforcement #474.

8 days ago

Rejected the synthetic call-result interception candidate after differential and production gating; no code from it is being kept.

Evidence on current origin/main parent plus the candidate:

  • Exact issue oracle: candidate matched TypeScript 6.0.3 and native TypeScript 7.0.2 with exactly two TS2322 diagnostics (string -> number and number -> string).
  • Extended oracle: candidate matched TypeScript 6.0.3 on rejection-branch unioning, PromiseLike assimilation, mutable Promise.all tuple output, per-position methods, and local Promise shadowing (same four diagnostic codes/messages).
  • Focused checker tests passed under the fixed 3,840 MB process-tree guard.
  • Initial full checker gate exposed 3 regressions (4,367/4,370): custom Promise overloads and non-literal Promise.all(items.map(...)). After narrowing ownership and non-literal handling, all 4,370 checker tests passed (2,051 MB peak).
  • Production gate disproved the design: frozen parent checked pinned Zod 4.5.2 with 150 diagnostics in 8.634 s (586 MB peak), while the candidate timed out at 300 s. Two successively narrower variants still timed out at 120 s and 90 s. The guard cleaned each run and memory stayed below 3,840 MB.

Root architectural finding: eagerly synthesizing Promise call results in the generic call-expression path can re-enter/reify large callback and Promise graphs even when the narrow surface oracle is correct. Cheap syntactic/ownership gates did not eliminate the production slowdown. The durable fix should live in generic Promise signature instantiation/substitution (or a memoized generic built-in surface), not a post-call result interceptor. The patch and tests were fully reverted; only the pre-existing user-owned untracked packages/runtime/test/test.txt remains.

8 days ago

Implemented and pushed directly to main in bd8c90eae.

  • Promise then / catch / finally result inference is evaluated at the call site, preserving fulfilled and rejected payloads without checker-local generic state leaking into the shared structural Promise graph.
  • Awaited distributes across union members, and Promise.all([... ] as const) retains readonly tuple positions.
  • Recursive iterable-element analysis now copies interner member IDs before recursion, fixing the ReleaseSafe stale-slice crash exposed by the Zod gate.
  • Focused Promise controls pass, including the exact TS2322 chain and readonly-tuple cases.
  • Full checker suite: 4,410 / 4,410.
  • ReleaseSafe Zod 4.5.2 21-file core proxy: exactly the shared core/index.ts:11:26 TS2307, 0 added diagnostics, no crash.
  • Final guarded peaks: checker 2,258 MB; ReleaseSafe build 2,627 MB; Zod 1,360 MB.

The README and detailed untimed validation record were updated in the same three-file commit. No timing claim was added.

Sign in to comment on this issue.