Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

[2.0 / next] `probing` leaks into a memo's pass pulled inside `isPending` — a nested `latest()` joins the pass to the hold

Abierto
#4,429 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
typescript
Área
frontend

Línea de trabajo

Start with the reader-seat rebuild in issue #4395 and the S1 PR, which the issue identifies as deciding a pulled pass’s world from its seat and the read’s posture. Reproduce with async-gen seed 7, case #1691, in packages/web/test/async-gen. Trace how verdict, probing, and probeFound are handled around a pulled memo’s pass; done means the pass’s world and probe answer reflect its own reads, not the outer isPending window.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Summary

isPending(fn) sets the module flag probing for the duration of fn. When a read inside fn pulls a dirty memo, verdictValue closes the window for the memo's pass (setVerdict(null) — A31: "the pulled memo's pass is its own, not the probe's") but leaves probing set. If that pass calls latest() itself, latest re-opens verdictValue with probing still true, and the pulled memo's read is treated as the outer probe's:

  • a held node born into the future (CONFIG_HELD, uninitialized, staged) read through latest() takes the probing && owned arm and joinPasses the pulled memo to the hold (REACTIVE_JOINED) — the memo's pass joins a world its own read (latest, a verdict read) does not derive from;
  • probeFound is set from inside the pulled pass (heldNotFinal, flying, a live affects() mark), so the outer isPending answers for reads the pulled memo made, not for the memo it read.

The same memo pulled by a plain read (or already clean) does neither. The pass's world depends on who woke it.

Reproduction

async-gen seed 7 case #1691 (packages/web/test/async-gen):

const sc = fc.sample(scenarioArb(), { seed: 7, numRuns: 1692 })[1691];
await runScenario(sc);
[s0=0 m0=memo(s0) m1=memo(latest(m0)+m0 !%4=1) m2=memo(s0 !%4=2)]
<Show when=latest(m2) fb>
  <span split[isPending(m0),isPending(s0)]>m0()</span>
  <span split[latest(s0)]>isPending(m1)</span>
</Show>
:: t A(s0=1) s0=2 s0=3 f s0=0 t s0=1 s0=3 m t m
scenario JSON
{"signals":[0],"memos":[{"async":false,"deps":[{"src":{"k":"s","i":0},"how":"plain"}],"poison":null},{"async":false,"deps":[{"src":{"k":"m","i":0},"how":"latest"},{"src":{"k":"m","i":0},"how":"plain"}],"poison":1},{"async":false,"deps":[{"src":{"k":"s","i":0},"how":"plain"}],"poison":2}],"tree":{"t":"show","when":{"how":"latest","src":{"k":"m","i":2}},"child":{"t":"group","kids":[{"t":"leaf","text":[{"how":"plain","src":{"k":"m","i":0}}],"attrs":[{"how":"pending","src":{"k":"m","i":0}},{"how":"pending","src":{"k":"s","i":0}}],"grouped":false},{"t":"leaf","text":[{"how":"pending","src":{"k":"m","i":1}}],"attrs":[{"how":"latest","src":{"k":"s","i":0}}],"grouped":false}]},"fallback":true},"steps":[{"t":"task"},{"t":"action","s":0,"v":1},{"t":"write","s":0,"v":2},{"t":"write","s":0,"v":3},{"t":"flush"},{"t":"write","s":0,"v":0},{"t":"task"},{"t":"write","s":0,"v":1},{"t":"write","s":0,"v":3},{"t":"micro"},{"t":"task"},{"t":"micro"}]}

isPending(m1) in a render effect pulls m1; m1's pass calls latest(m0) with m0 held and uninitialized in the flush's own transaction (born into it). Stack: joinPass < verdictValue (the uninitialized-held arm, probing) < read < latest < m1's fn < recompute < pullComputed < verdictValue < read < isPending.

Found by the S1 reader-seat differential (SEAT_SHADOW_REPORT): class Vl4 P/P — world after the read (old 2, new 0; Vl posture; s h u · vseat ahead oldVlane flushTx flushIsElTx), 1 instance at 2k cases × seed 7; none in the signals, solid or web suites. The slot agrees (the staging, P); the pass's world does not. No end-screen difference in this case.

Why it is a bug

  • A31: a memo's pass pulled inside a window is its own; the window answers for what the pass produced. verdictValue applies this to the dispatch (setVerdict(null)) and not to probing/probeFound, so a nested latest() inside the pulled pass re-enters the window with the outer probe's state.
  • F-vworld (2026-10-29): a verdict read leaves marks and does not raise the pass's world; latest() of a born-into-the-future node is a verdict read whichever pass makes it.

Intended fix

The reader-seat rebuild S1 (#4395 → S1 PR) decides the pass's world from its seat and the read's posture (row Vl4), not from a flag the puller left set. Until the swap lands, the shadow carries this as a known class (shadowKnown, this issue). (probeFound's leak is the same bug's other half; the differential compares worlds and slots, not probe answers — the swap should save/clear probing and probeFound around the pulled pass as it does verdict.)

Lenguaje dominante
TypeScript
Estrellas
36.2k
Forks
1.1k
Merge medio
14 h 5 min
PR fusionados (30 d)
400

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de solidjs/solid

Todos los issues de solidjs/solid

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.