@polyengine/runtime 0.1.0-pre.g633468a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +23 -0
  3. package/esm/cabi/async_values.js +162 -0
  4. package/esm/cabi/bulk_lists.js +198 -0
  5. package/esm/cabi/context.js +42 -0
  6. package/esm/cabi/flatten.js +145 -0
  7. package/esm/cabi/float.js +67 -0
  8. package/esm/cabi/handles.js +253 -0
  9. package/esm/cabi/layout.js +149 -0
  10. package/esm/cabi/lift.js +196 -0
  11. package/esm/cabi/load.js +146 -0
  12. package/esm/cabi/lower.js +141 -0
  13. package/esm/cabi/memory.js +182 -0
  14. package/esm/cabi/mod.js +22 -0
  15. package/esm/cabi/store.js +186 -0
  16. package/esm/cabi/strings.js +336 -0
  17. package/esm/cabi/trap.js +38 -0
  18. package/esm/cabi/types.js +264 -0
  19. package/esm/cabi/values.js +64 -0
  20. package/esm/cache/core.js +156 -0
  21. package/esm/cache/dir.js +170 -0
  22. package/esm/cache/mod.js +4 -0
  23. package/esm/cache/web.js +136 -0
  24. package/esm/digest/digest.js +332 -0
  25. package/esm/digest/mod.js +3 -0
  26. package/esm/digest/verify.js +129 -0
  27. package/esm/embedder/casing.js +56 -0
  28. package/esm/embedder/copy.js +42 -0
  29. package/esm/embedder/errors.js +26 -0
  30. package/esm/embedder/imports.js +63 -0
  31. package/esm/embedder/instantiate.js +978 -0
  32. package/esm/embedder/mod.js +40 -0
  33. package/esm/embedder/resources.js +406 -0
  34. package/esm/embedder/streams.js +770 -0
  35. package/esm/embedder/values.js +450 -0
  36. package/esm/embedder/version.js +273 -0
  37. package/esm/exec/boundary.js +1902 -0
  38. package/esm/exec/executor.js +1004 -0
  39. package/esm/exec/host_streams.js +818 -0
  40. package/esm/exec/mod.js +4 -0
  41. package/esm/intrinsics/async_builtins.js +510 -0
  42. package/esm/intrinsics/context.js +90 -0
  43. package/esm/intrinsics/errors.js +13 -0
  44. package/esm/intrinsics/fact_calls.js +865 -0
  45. package/esm/intrinsics/mod.js +564 -0
  46. package/esm/intrinsics/stream_builtins.js +578 -0
  47. package/esm/intrinsics/transcode.js +443 -0
  48. package/esm/jspi/bridge.js +579 -0
  49. package/esm/jspi/mechanics.js +89 -0
  50. package/esm/jspi/mod.js +5 -0
  51. package/esm/jspi/suspending.js +15 -0
  52. package/esm/jspi/types.js +29 -0
  53. package/esm/package.json +3 -0
  54. package/esm/plan/format.js +5 -0
  55. package/esm/plan/loader.js +657 -0
  56. package/esm/plan/mod.js +3 -0
  57. package/esm/shim/mod.js +2 -0
  58. package/esm/shim/translator.js +94 -0
  59. package/esm/task/mod.js +483 -0
  60. package/esm/task/scheduler.js +1028 -0
  61. package/esm/task/streams.js +786 -0
  62. package/esm/task/subtask.js +135 -0
  63. package/esm/task/thread.js +255 -0
  64. package/esm/task/waitable.js +144 -0
  65. package/package.json +91 -0
  66. package/types/cabi/async_values.d.ts +35 -0
  67. package/types/cabi/bulk_lists.d.ts +18 -0
  68. package/types/cabi/context.d.ts +59 -0
  69. package/types/cabi/flatten.d.ts +14 -0
  70. package/types/cabi/float.d.ts +14 -0
  71. package/types/cabi/handles.d.ts +70 -0
  72. package/types/cabi/layout.d.ts +13 -0
  73. package/types/cabi/lift.d.ts +25 -0
  74. package/types/cabi/load.d.ts +14 -0
  75. package/types/cabi/lower.d.ts +10 -0
  76. package/types/cabi/memory.d.ts +58 -0
  77. package/types/cabi/mod.d.ts +15 -0
  78. package/types/cabi/store.d.ts +12 -0
  79. package/types/cabi/strings.d.ts +23 -0
  80. package/types/cabi/trap.d.ts +11 -0
  81. package/types/cabi/types.d.ts +206 -0
  82. package/types/cabi/values.d.ts +5 -0
  83. package/types/cache/core.d.ts +97 -0
  84. package/types/cache/dir.d.ts +6 -0
  85. package/types/cache/mod.d.ts +3 -0
  86. package/types/cache/web.d.ts +10 -0
  87. package/types/digest/digest.d.ts +17 -0
  88. package/types/digest/mod.d.ts +2 -0
  89. package/types/digest/verify.d.ts +48 -0
  90. package/types/embedder/casing.d.ts +40 -0
  91. package/types/embedder/copy.d.ts +24 -0
  92. package/types/embedder/errors.d.ts +11 -0
  93. package/types/embedder/imports.d.ts +47 -0
  94. package/types/embedder/instantiate.d.ts +88 -0
  95. package/types/embedder/mod.d.ts +11 -0
  96. package/types/embedder/resources.d.ts +158 -0
  97. package/types/embedder/streams.d.ts +202 -0
  98. package/types/embedder/values.d.ts +70 -0
  99. package/types/embedder/version.d.ts +85 -0
  100. package/types/exec/boundary.d.ts +360 -0
  101. package/types/exec/executor.d.ts +125 -0
  102. package/types/exec/host_streams.d.ts +165 -0
  103. package/types/exec/mod.d.ts +3 -0
  104. package/types/intrinsics/async_builtins.d.ts +69 -0
  105. package/types/intrinsics/context.d.ts +28 -0
  106. package/types/intrinsics/errors.d.ts +5 -0
  107. package/types/intrinsics/fact_calls.d.ts +120 -0
  108. package/types/intrinsics/mod.d.ts +187 -0
  109. package/types/intrinsics/stream_builtins.d.ts +113 -0
  110. package/types/intrinsics/transcode.d.ts +21 -0
  111. package/types/jspi/bridge.d.ts +227 -0
  112. package/types/jspi/mechanics.d.ts +50 -0
  113. package/types/jspi/mod.d.ts +3 -0
  114. package/types/jspi/suspending.d.ts +1 -0
  115. package/types/jspi/types.d.ts +26 -0
  116. package/types/plan/format.d.ts +369 -0
  117. package/types/plan/loader.d.ts +113 -0
  118. package/types/plan/mod.d.ts +2 -0
  119. package/types/shim/mod.d.ts +1 -0
  120. package/types/shim/translator.d.ts +55 -0
  121. package/types/task/mod.d.ts +257 -0
  122. package/types/task/scheduler.d.ts +421 -0
  123. package/types/task/streams.d.ts +370 -0
  124. package/types/task/subtask.d.ts +96 -0
  125. package/types/task/thread.d.ts +73 -0
  126. package/types/task/waitable.d.ts +67 -0
@@ -0,0 +1,421 @@
1
+ /** definitions.py `Cancelled` (line 248). */
2
+ export declare const CANCELLED_FALSE = false;
3
+ export declare const CANCELLED_TRUE = true;
4
+ export type Cancelled = boolean;
5
+ /**
6
+ * What a thread body yields when it wants to stop running. Mirrors the
7
+ * reference's `Thread.wait_until` (`ready_func` + `cancellable`); `suspend`
8
+ * is `wait_until` with no ready condition (`ready_func === null`), and
9
+ * `yield_` is `wait_until(() => true)`.
10
+ */
11
+ export interface BlockRequest {
12
+ /** Resumable once this returns true; `null` = only an explicit resume. */
13
+ readyFunc: (() => boolean) | null;
14
+ cancellable: boolean;
15
+ /**
16
+ * JSPI seam. When present the thread is not waiting on a scheduler
17
+ * condition at all — it is waiting for a **Promise**, namely the one a
18
+ * `promising`-wrapped wasm entry returned. The driving loop awaits it and
19
+ * resumes the body with the resolved value (or throws the rejection into
20
+ * the body, so a post-resume trap unwinds exactly like a synchronous one —
21
+ * jspi pin (e)).
22
+ *
23
+ * This is what lets one generator body serve both modes: in plain mode the
24
+ * core call returns a value and the body never yields such a request, so
25
+ * the synchronous path is bit-for-bit what it was before JSPI existed.
26
+ */
27
+ awaitValue?: Promise<unknown>;
28
+ }
29
+ /**
30
+ * A thread body: yields block requests, and receives back either the
31
+ * cancelled flag (for a scheduler block point) or the resolved value of an
32
+ * `awaitValue` request.
33
+ */
34
+ export type ThreadBody = Generator<BlockRequest, void, any>;
35
+ /**
36
+ * Failure raised where the reference genuinely needs to suspend a wasm frame.
37
+ *
38
+ * This is deliberately *not* a `Trap`: the component is not at fault and the
39
+ * program is not ill-formed — our runtime is incomplete. Reporting it as a
40
+ * trap would let a conformance run score a missing capability as a correct
41
+ * rejection, which is the exact failure mode contracts/plan-format.md's
42
+ * error-phase split exists to prevent.
43
+ */
44
+ export declare class NeedsJspi extends Error {
45
+ constructor(what: string);
46
+ }
47
+ export declare function needsJspi(what: string): never;
48
+ /**
49
+ * Failure raised where a capability scheduled for a later M2 phase is
50
+ * required. Same rationale as `NeedsJspi`: never a `Trap`.
51
+ */
52
+ export declare class PendingCapability extends Error {
53
+ constructor(what: string);
54
+ }
55
+ /** @internal — see `onInstancePoisoned`; registered once by task/streams.ts. */
56
+ export declare function setOnInstancePoisoned(f: (inst: {
57
+ handles: Iterable<unknown>;
58
+ }, cause: unknown) => void): void;
59
+ /**
60
+ * @internal — invoke the poisoning hook. For the bracket-break sites that
61
+ * live outside this module (`Thread.resumeWith`, exec/boundary.ts `poison`):
62
+ * one seam, all sites.
63
+ */
64
+ export declare function notifyInstancePoisoned(inst: {
65
+ handles: Iterable<unknown>;
66
+ }, cause: unknown): void;
67
+ export declare function isInstancePoisoned(inst: object): boolean;
68
+ /**
69
+ * May a settled activation tail for parked thread `t` be DISPATCHED now
70
+ * (issue #156)? True iff its instance is host-enterable — `Thread.resumeWith`
71
+ * brackets the resumption with `enterFrom(null)` — or POISONED, in which case
72
+ * `resumeWith`'s early return retires it and deferring would leak forever.
73
+ *
74
+ * CONTRACT: a parked entry without a reachable `task.inst` (the partial
75
+ * thread doubles the host-pump tests park in `Store.awaiting`) holds no
76
+ * reentrance state, so there is nothing to defer on: dispatchable.
77
+ */
78
+ export declare function dispatchableTail(t: any): boolean;
79
+ /**
80
+ * The recorded cause of an instance's poisoning: the original trap that
81
+ * broke the enter/leave bracket (polyengine#145). `undefined` when the instance
82
+ * is not poisoned — and, degenerately, when the poisoning cause itself was
83
+ * a thrown `undefined`; use `isInstancePoisoned` for the predicate.
84
+ */
85
+ export declare function instancePoisonCause(inst: object): unknown;
86
+ /**
87
+ * Append the recorded poison cause to an entry-refusal trap message
88
+ * (polyengine#145 ask 1). "cannot enter component instance" covers two states
89
+ * that send an embedder down entirely different debugging paths — a
90
+ * transient reentrance overlap (retry later, look for caller-side call
91
+ * overlap) and a permanently poisoned instance (the corpse of an earlier
92
+ * trap, which this suffix names). Only the poisoned case gets the suffix:
93
+ * the transient message stays byte-identical, and the suffix is
94
+ * conformance-safe because the official suite matches trap messages by
95
+ * substring (harness/src/runner.ts).
96
+ */
97
+ export declare function withPoisonCause(inst: object, base: string): string;
98
+ /** Test hook: switch policy at runtime. `null` restores FIFO. */
99
+ export declare function schedulerSeedForTesting(value: number | null): void;
100
+ export declare function schedulerPolicy(): "fifo" | "seeded-shuffle";
101
+ /**
102
+ * Pick one candidate. FIFO (index 0 — candidates are supplied in
103
+ * ready-order) unless a seed is configured, in which case a seeded uniform
104
+ * choice, mirroring the reference's `random.choice`.
105
+ */
106
+ export declare function chooseCandidate<T>(candidates: readonly T[]): T;
107
+ export interface CurrentThreadLike {
108
+ storage: number[];
109
+ task: any;
110
+ }
111
+ export declare function pushCurrentThread(t: CurrentThreadLike): void;
112
+ export declare function popCurrentThread(t: CurrentThreadLike): void;
113
+ /**
114
+ * Run `fn` with `t` as the ambient, for `fn`'s SYNCHRONOUS extent.
115
+ *
116
+ * This is the wasm-entry bracket (`awaitCore`). It is the same `threadStack`
117
+ * the scheduler's own `resume()` bracket uses, deliberately: a wasm entry made
118
+ * from *inside* an engine-driven resumption (a FACT callee reached from a
119
+ * resumed activation — fact_calls.ts) has an empty scheduler bracket, and the
120
+ * entry itself is then the most specific statement of who is running.
121
+ */
122
+ export declare function withActivation<T>(t: any, fn: () => T): T;
123
+ /**
124
+ * Record that the engine will run `t`'s wasm outside our frames.
125
+ *
126
+ * Idempotent in MEMBERSHIP but not in POSITION: re-claiming MOVES an
127
+ * existing claim to the top. The stack's contract is "top = the innermost
128
+ * activation the engine is running outside our frames", and a re-claim is
129
+ * direct evidence that `t` is running RIGHT NOW (its Suspending import just
130
+ * returned into its wasm). The previous early-return kept stale order: a
131
+ * nested callee's claim whose release edge is a promise reaction
132
+ * (`Store.noteAwaiting` -> `releaseClaimOf`) outlives the callee by a
133
+ * microtask, and an outer activation's continuation chunk that resumed in
134
+ * that window re-claimed itself as a NOOP — leaving the finished callee on
135
+ * top, so every ambient read in the rest of the chunk (the next hop's
136
+ * `owner` capture, and any unsafe intrinsic like `context.set`, which has
137
+ * no hop to re-anchor on) answered the wrong thread. Found as issue #24:
138
+ * wit-bindgen's callback epilogue restored its task pointer into another
139
+ * thread's context slots, and the next disciplined callback invocation
140
+ * panicked on a null slot (async_support.rs:578).
141
+ *
142
+ * A null/undefined activation is "no claim" — the instantiation-time shape
143
+ * that has no thread at all.
144
+ */
145
+ export declare function claimActivationAmbient(t: any): void;
146
+ export declare function dbgId(t: unknown): string;
147
+ /**
148
+ * Drop `t`'s activation-ambient claim, if it holds one.
149
+ *
150
+ * The two closing edges: the activation PARKS on a fresh suspension
151
+ * (`blockCurrentActivation`), or it FINISHES — its `awaitValue` promise
152
+ * settles, normally or by rejection, and `Store.noteAwaiting`'s eager settle
153
+ * continuation calls this. The `task.implicitThread` indirection covers the
154
+ * second edge for claims taken against a task's implicit thread.
155
+ */
156
+ export declare function releaseActivationAmbient(t: any): void;
157
+ /** Claim the ambient for `t` across an engine-driven resumption. */
158
+ export declare function setResumingThread(t: any): void;
159
+ /** Is a settled-but-not-yet-run activation holding the ambient? */
160
+ export declare function hasResumingThread(): boolean;
161
+ /** Release the claim; called once we are back in our own continuation. */
162
+ export declare function clearResumingThread(): void;
163
+ /**
164
+ * Release the driver's claim iff its activation is demonstrably RUNNING —
165
+ * i.e. the claim names the same thread the ACTIVATION AMBIENT names for the
166
+ * code calling us. The claim exists to cover the window between settling a
167
+ * suspension and the resumed activation running; once that activation's own
168
+ * code is on the stack the window is closed, and holding the claim would
169
+ * falsely trip the one-claimant assert when the running activation's built-in
170
+ * settles ANOTHER activation's suspension — `subtask.cancel` delivering a
171
+ * cancellation to a parked callee (cancellable.wast) is exactly that shape.
172
+ * When the two disagree (or no ambient is present) the claim stays, and the
173
+ * assert keeps guarding the genuine two-unrun-claimants bug it was built for.
174
+ *
175
+ * The comparison used to be against the async-context store; it is now
176
+ * against `activationOf()`, which is the same statement made explicitly.
177
+ */
178
+ export declare function consumeClaimIfRunning(): void;
179
+ /**
180
+ * Release the claim iff it names `t` — the settle-side half of the claim
181
+ * discipline: a claim taken when `t`'s suspension was settled dies when `t`'s
182
+ * activation finishes (its `awaitValue` promise settles; `Store.noteAwaiting`
183
+ * calls this from the eager settle continuation) or parks again
184
+ * (`blockCurrentActivation` consumes via `consumeClaimIfRunning`).
185
+ *
186
+ * `t` FINISHING also ends its activation ambient, so both are dropped here.
187
+ */
188
+ export declare function releaseClaimOf(t: any): void;
189
+ /** Diagnostic (#24 probe): the full ambient state, for tracing. */
190
+ export declare function ambientDebug(): {
191
+ stack: unknown[];
192
+ claims: unknown[];
193
+ resuming: unknown;
194
+ };
195
+ /** Diagnostic: module-scope state that must NOT survive a completed call. */
196
+ export declare function ambientResidue(): {
197
+ stack: number;
198
+ claim: boolean;
199
+ };
200
+ export declare function currentThread<T = CurrentThreadLike>(): T;
201
+ export declare function maybeCurrentThread(): CurrentThreadLike | undefined;
202
+ /** definitions.py `current_task()` (line 309). */
203
+ export declare function currentTask(): any;
204
+ /**
205
+ * The running task, or `null` outside any task — e.g. a core module's start
206
+ * function during instantiation, which the reference has no model for.
207
+ */
208
+ export declare function maybeCurrentTask(): any | null;
209
+ /** definitions.py `current_instance()` (line 312). */
210
+ export declare function currentInstance(): any;
211
+ /** Structural view of a Thread, as the store's ready queue needs it. */
212
+ export interface SchedulableThread {
213
+ ready(): boolean;
214
+ waiting(): boolean;
215
+ resume(cancelled?: Cancelled): void;
216
+ task: any;
217
+ }
218
+ /**
219
+ * The embedder-visible scheduler state (definitions.py `Store`). One per
220
+ * instantiated component in this runtime — the reference shares one `Store`
221
+ * across component instances of a linked graph, and so do we: `Executor`
222
+ * creates a single `Store` and hands it to every `ComponentInstanceState`.
223
+ *
224
+ * `waiting` is kept as an **array, in insertion order**, which is what makes
225
+ * the default policy FIFO: `readyCandidates()` preserves the order in which
226
+ * threads started waiting.
227
+ */
228
+ export declare class Store {
229
+ readonly waiting: SchedulableThread[];
230
+ /**
231
+ * Host-import promises this store is waiting on. Non-empty means progress
232
+ * is possible but only after a microtask turn — see `drive` in
233
+ * exec/boundary.ts. (definitions.py has no analogue: its host functions run
234
+ * on real threads.)
235
+ */
236
+ readonly pendingHostCalls: Set<Promise<unknown>>;
237
+ /**
238
+ * An exception raised by a host import's promise (a rejection, or a trap
239
+ * thrown while lowering its results). It cannot propagate out of the
240
+ * microtask that produced it, so it is parked here and rethrown by whoever
241
+ * is driving the store — which is the call the guest is blocked in.
242
+ */
243
+ hostFailure: unknown;
244
+ startWaiting(t: SchedulableThread): void;
245
+ stopWaiting(t: SchedulableThread): void;
246
+ /** Ready waiting threads, in wait order (the FIFO of the default policy). */
247
+ readyCandidates(): SchedulableThread[];
248
+ /**
249
+ * Threads parked on a Promise (the jspi `awaitValue` seam). They are not in
250
+ * `waiting` — nothing the scheduler can do makes them ready — so the driving
251
+ * loop tracks them separately and resumes them when their promise settles.
252
+ */
253
+ readonly awaiting: Set<any>;
254
+ /**
255
+ * Settled-but-unserviced activation tails, in settle order.
256
+ *
257
+ * A settled `awaitValue` is the rest of an activation that already finished
258
+ * its wasm: result shaping, the callback loop, `exit_implicit_thread` (and
259
+ * with it the exclusive-thread release). The reference runs all of that
260
+ * atomically inside `Thread.resume`; under jspi it lands a few engine
261
+ * microtasks after the observable effects of the activation (`task.return`
262
+ * flips `resolved` DURING the wasm, the settle only afterwards — jspi
263
+ * pin (j)). Any scheduling decision taken in that window sees phantom
264
+ * state — a finished callee still "holding" its exclusive slot made
265
+ * cancellable.wast report STARTING for an entry the reference admits. So
266
+ * settlement is recorded EAGERLY (at park time, below), `tick` refuses to
267
+ * run anything while a tail is unserviced, and the driving loop services
268
+ * this queue first.
269
+ */
270
+ readonly settled: {
271
+ t: any;
272
+ value: unknown;
273
+ failure: {
274
+ error: unknown;
275
+ } | undefined;
276
+ }[];
277
+ /**
278
+ * Park `t` on `promise` (jspi `awaitValue`), with EAGER settle tracking.
279
+ *
280
+ * The `.then` here is also what closes the claim discipline for
281
+ * resumptions the driver did not settle itself (a guest built-in resolving
282
+ * another activation's suspension — `subtask.cancel` delivering a
283
+ * cancellation): the claim taken at settle time must survive until the
284
+ * resumed activation parks again or finishes, and "finished" is exactly
285
+ * this continuation firing. See `releaseClaimOf`.
286
+ */
287
+ noteAwaiting(t: any, promise: Promise<unknown>): void;
288
+ /**
289
+ * Service settled activation tails. Returns whether anything ran. EVERY
290
+ * driving loop must call this before (and interleaved with) `tick` — the
291
+ * queue gates `tick`, so a driver that never services it wedges the store
292
+ * (observed: host-stream pumping between export calls). A `resumeWith` may
293
+ * throw (trap unwinding); callers propagate or park it exactly as they do
294
+ * for `tick`.
295
+ *
296
+ * A tail whose instance is NOT host-enterable is DEFERRED IN PLACE — left
297
+ * in the queue, skipped here — until the lock releases (issue #156).
298
+ * `resumeWith` brackets the resumption with `enterFrom(null)`, and under
299
+ * the shared synthetic per-instantiation root a host entry into ANY
300
+ * instance of the graph locks the root, so while one instance is entered a
301
+ * sibling's tail cannot be dispatched: dispatching it tripped
302
+ * `resumeWith`'s enterability assert (which, mutating before asserting,
303
+ * also stranded the thread and lost the settle).
304
+ *
305
+ * Deferral is safe because `!inst.mayEnterFrom(null)` is EXACTLY `tick`'s
306
+ * candidate-filter predicate on the same instance: while a tail of `inst`
307
+ * is deferred, `tick` cannot resume any thread of `inst` either, so the
308
+ * phantom-state gate the queue exists to enforce is preserved per-instance
309
+ * by construction.
310
+ *
311
+ * The ordering discipline is therefore per-instance settle order. Cross-
312
+ * instance order relaxes only when enterability defers a tail, which is
313
+ * conforming schedule nondeterminism: in definitions.py the tail runs
314
+ * atomically inside the entered bracket, so a host entry admitted during a
315
+ * park necessarily orders before the parked activation's tail there.
316
+ *
317
+ * A POISONED instance's tail is still dispatched: `resumeWith`'s poison
318
+ * early-return retires it, and deferring it would leak forever — a
319
+ * poisoned leaf keeps its lock permanently.
320
+ */
321
+ serviceSettled(): boolean;
322
+ /**
323
+ * "Would a `serviceSettled` call make progress right now?" — i.e. some
324
+ * entry is stale (would be removed) or serviceable (would be dispatched).
325
+ * A queue holding ONLY deferred tails (issue #156) answers false: `tick`
326
+ * must not be gated by them, and the driving loops must not spin on them.
327
+ */
328
+ hasServiceableSettled(): boolean;
329
+ /**
330
+ * "Does component instance `inst` still have runnable work?" — the
331
+ * drain-to-quiescence predicate behind the **deferred entry decision**
332
+ * (issue #43).
333
+ *
334
+ * wasmtime decides an async-lowered call's initial status only after the
335
+ * executor has drained the work queued ahead of it: a queued
336
+ * `GuestCall(StartImplicit)` is popped, and if `is_ready` is false
337
+ * (`do_not_enter || backpressure`) the caller is told STARTING
338
+ * (concurrent.rs :1497-1522, :3040-3160). That formulation is FIFO-order
339
+ * dependent; polyengine uses the order-robust restatement (issue #43): *the
340
+ * call reports STARTING only if the callee is still unstarted after the
341
+ * instance's runnable work has been exhausted* — drain to quiescence, not
342
+ * pop-one. That is what keeps `sync-streams.wast` green under
343
+ * `POLYENGINE_SCHED_SEED` shuffles, which wasmtime's own rule would not be.
344
+ * Adjudicated 2026-08-10 (issue #43): entry-status timing is NOT
345
+ * normative — this predicate implements a scheduler *policy*, picked so
346
+ * the suite's schedule-overfitted STARTED assertion holds under any
347
+ * seed; the hold-rule gate itself is the spec semantics.
348
+ *
349
+ * "Runnable work of `inst`" is, exhaustively:
350
+ *
351
+ * (a) a settled-but-unserviced activation tail (`settled`) — bookkeeping
352
+ * the reference runs atomically inside `Thread.resume`, so the
353
+ * instance is mid-step, not quiescent;
354
+ * (b) a waiting entry (thread or `SuspensionPoint`) of `inst` that is
355
+ * `ready()` — the scheduler will resume it on the next tick. A gate
356
+ * holder parked mid-frame on an un-rendezvous'd operation is NOT
357
+ * ready and therefore contributes nothing: that is the "holder
358
+ * cannot be drained" case, whose answer is STARTING;
359
+ * (c) a thread of `inst` in `awaiting` whose promise is not a scheduler
360
+ * park — i.e. genuinely in flight across an engine microtask hop.
361
+ * A JSPI-parked activation appears in `awaiting` *and* owns a
362
+ * `SuspensionPoint` in `waiting` (`SuspensionPoint.owner`), and is
363
+ * accounted for by (b) instead; counting it here would make the
364
+ * instance permanently non-quiescent.
365
+ *
366
+ * `excludeTask` is the CALLER's task, and is excluded everywhere: the
367
+ * caller cannot be drained — it is the activation asking the question.
368
+ * This is what makes the "only obstacle is the current running activation"
369
+ * shape (a nested lower from inside the gate holder's own invocation)
370
+ * answer STARTING immediately, with no park at all.
371
+ */
372
+ hasRunnableWork(inst: unknown, excludeTask: unknown): boolean;
373
+ /**
374
+ * definitions.py `Store.tick` (line 597): resume one ready thread, bracketed
375
+ * by the reentrance gate for a host-initiated entry (`enter_from(None)` /
376
+ * `leave_to(None)`).
377
+ *
378
+ * Returns false when no thread was ready, so callers can distinguish
379
+ * "made progress" from "stuck" without inspecting the queue themselves.
380
+ */
381
+ tick(): boolean;
382
+ }
383
+ /** Mark `p` as an activity arm (exec/host_streams.ts `HostActivity`). */
384
+ export declare function markHostActivityArm(p: Promise<unknown>): void;
385
+ /** Is there host-call work outstanding that is not just an activity arm? */
386
+ export declare function hasRealHostCall(store: Store): boolean;
387
+ /** Every outstanding host call that is real work (not an activity arm). */
388
+ export declare function realHostCalls(store: Store): Promise<unknown>[];
389
+ /**
390
+ * Is there anything left that only a turn of the event loop could advance?
391
+ * Activity arms do not count: they say "the embedder may still act", which is
392
+ * precisely the state in which a between-calls driver should stop and let the
393
+ * operation's promise stay pending (the documented hang, exec/host_streams.ts
394
+ * module header).
395
+ *
396
+ * `store.settled` (settled-but-unserviced activation tails) DOES count: it
397
+ * gates `tick`, so exiting with a tail queued is a lost wakeup — the store is
398
+ * wedged until some other driver appears.
399
+ */
400
+ export declare function storeQuiescent(store: Store): boolean;
401
+ /**
402
+ * The reference's `canon_lift` sync driving loop (line 2213):
403
+ *
404
+ * ```python
405
+ * while task.state != Task.State.RESOLVED:
406
+ * candidates = { t for t in inst.threads if t.ready() and t is not inst.exclusive_thread }
407
+ * trap_if(not candidates)
408
+ * random.choice(list(candidates)).resume()
409
+ * ```
410
+ *
411
+ * Note the candidate set is `inst.threads` — threads *of the callee instance*
412
+ * — and excludes the exclusive thread, and that an empty set is a **trap**
413
+ * (the spec's deadlock trap), not a hang.
414
+ */
415
+ export declare function driveSyncLift(task: {
416
+ state: string;
417
+ inst: {
418
+ threads: Iterable<SchedulableThread>;
419
+ exclusiveThread: unknown;
420
+ };
421
+ }): void;