L2: mount triggered by latest() shows content beside the committed value
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 55/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 provided failing vitest repro under packages/signals/tests, run it to confirm slot reads "content 1" while screenX stays 0. Then read the first-pass lane rule in recompute (packages/signals/src/core/core.ts) alongside the SPEC's lane rule and A29 boundary-scope note to see why a pass created inside a verdict lane inherits that lane instead of staying mainline. Done means the repro passes (slot equals "fallback" during the hold) without breaking the existing lane, loading-boundary and optimistic-lane tests — expect to consult the SPEC draft for proto/3540-boundary-scope before touching semantics.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Inside an action, a <Show when={latest(x) > 0}> opens at once, which is expected, because latest() is display-ahead. But the new <Loading> it mounts shows content 1 while everything else on screen still shows the committed x = 0. Under the lane rule, a verdict lane's mounts stay mainline, so the mounted content belongs to the screen's world, not the proposal's. This is a torn frame.
Repro (packages/signals/tests, vitest)
import { expect, it } from "vitest";
import {
action,
createLoadingBoundary,
createMemo,
createRenderEffect,
createRoot,
createSignal,
flush,
latest,
untrack
} from "../src/index.js";
const settle = async () => {
for (let i = 0; i < 3; i++) {
await new Promise(r => setTimeout(r, 0));
flush();
}
};
it("a mount triggered by latest() shows its content beside the committed value", async () => {
const [x, setX] = createSignal(0);
let screenX: unknown;
let slot: unknown;
createRoot(() => {
// <p>{x()}</p>
createRenderEffect(x, v => {
screenX = v;
});
// <Show when={latest(x) > 0}>
// <Loading fallback="fallback">content {x()}</Loading>
// </Show>
const when = createMemo(() => latest(x) > 0);
const children = createMemo(() =>
when()
? untrack(() =>
createLoadingBoundary(
() => `content ${x()}`,
() => "fallback"
)
)
: "closed"
);
createRenderEffect(
() => {
const c = children();
return typeof c === "function" ? c() : c;
},
v => {
slot = v;
}
);
});
flush();
expect([screenX, slot]).toEqual([0, "closed"]);
let release!: () => void;
const done = action(function* () {
setX(1);
yield new Promise<void>(r => (release = r));
})();
await settle();
// The action holds x = 1; the screen still shows x = 0.
expect(screenX).toBe(0);
// Actual: "content 1" beside the committed 0.
expect(slot).toBe("fallback");
release();
await done;
await settle();
expect([screenX, slot]).toEqual([1, "content 1"]);
});
The repro fails at expect(slot).toBe("fallback") with Received: "content 1". The same thing happens when the content is a memo, createMemo(() => \content ${x()}`)`.
Expected vs actual
| Checkpoint | Expected | Actual |
|---|---|---|
| Before the action | x = 0, slot closed |
x = 0, slot closed |
Action holds x = 1 |
x = 0, slot fallback |
x = 0, slot content 1 |
| Action ends | x = 1, slot content 1 |
x = 1, slot content 1 |
Rules involved
- The lane rule: an optimistic lane sees the screen plus its own guesses, and its mounts' children are the lane's. A verdict lane computes the real outcome, and its mounts stay mainline. Here the
Showcondition is verdict-lane work, and its children are seated in that lane. The newLoading's first pass inherits the lane from the pass that created it, so it reads the proposalx = 1. - A29's boundary scope (2026-10-06): a loading boundary that has not shown content owns its subtree. Content that reads a hold waits behind the boundary's fallback, and it appears at the hold's commit.
- No tearing: content on screen agrees with the screen's value of what it reads.
The suspected site is the first-pass lane rule in recompute (core/core.ts). A first pass takes its creator's lane, which is right for an optimistic lane (ruling A). For a verdict lane, it should stay mainline.
Where it reproduces
next(ba67ddbaa)- #3835's branch (
ae59c7682) - #3824's head (
3ab71b0dc) proto/3540-boundary-scope(60d2f1ded). Its SPEC draft already lists this shape as open ("a boundary a verdict reader mounts … shows the held derivation now rather than its fallback").
Provenance
Found by the semantic fuzzer's mount-under-hold cohort, verdict family (fuzz/semantic-fuzzer-l2, b2bdf125a, rules MH1 and MH5). It probably shares a root with the read-order mount family (fuzzer cases 827, 416 and 936). There, a latest() read routes a freshly mounted child into the verdict lane, its mount control publishes alone, and the slot stays empty. In both cases a pass picks up verdict-lane membership that the lane rule says stays mainline: through a read in the read-order family, and through its creator here.
- Lenguaje dominante
- TypeScript
- Estrellas
- 36.1k
- Forks
- 1.1k
- Merge medio
- 11 h 42 min
- PR fusionados (30 d)
- 326
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de solidjs/solid
-
[2.0 next, regressed after rc.13] Async-generator action times out on its authoritative live echoAbierto
Dificultad 3/5 1-2 días Aptitud para principiantes 55/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 Medio día Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 64/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
Los mantenedores suelen responder en 1 día
Todos los issues de solidjs/solid
Issues similares
-
[bug] diagnostics.dumpBody:Buffer 形态请求(透传 lane)跳过 dumps/ 落盘,仅留 raw/-unknown-Posiblemente ocupada @ranxianglei la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
ranxianglei/billion-context#2421 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
platformatic/mcp#216 ·
Los mantenedores suelen responder en 1 día
-
pending triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
nuxt/test-utils#1842 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
MoonshotAI/kimi-code#4146 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
farbenmeer/tapi#531 ·