원클릭으로
add-test
Use whenever adding an e2e compiler-output test (our JS vs the reference compiler) — a new cluster case under `tasks/compiler_tests/`.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use whenever adding an e2e compiler-output test (our JS vs the reference compiler) — a new cluster case under `tasks/compiler_tests/`.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
🧩 PARITY · Turn one divergence from the Original into a test per affected case (does not fix).
Use when starting any compiler task or session before touching parser/analyze/transform/ast/codegen/css/diagnostics code, before proposing a design or writing code, and when the task grows into a new layer and you need its PRD invariants.
🔪 SDT · verdict-directed review of backend codegen/transform. Use when auditing codegen/transform for smart-analyzer / dumb-codegen violations, or when porting a transform/codegen visitor from the Original.
The required unit-test format for `crates/**/src/**` — use when writing or reviewing a Rust `#[test]`, a test helper, or an `assert_*`. Not for `tasks/compiler_tests/` (use add-test) or `tasks/diagnostic_tests/` (use add-diagnostic-test).
PRD editorial standard for docs/. Use when writing a new PRD, extending one, or reviewing changes under docs/ — and when another skill needs the PRD rules.
Create a new diagnostic parity test case. Use when the user asks to add a diagnostic test, create a new `tasks/diagnostic_tests/cases/<name>` case, or capture a false positive/false negative against npm `svelte/compiler` before implementation.
| name | add-test |
| description | Use whenever adding an e2e compiler-output test (our JS vs the reference compiler) — a new cluster case under `tasks/compiler_tests/`. |
Single sanctioned path for an e2e output-comparison case. Adding such a test by hand —
creating the dir, writing case.svelte, or registering compiler_case! directly — is
not allowed; go through these steps.
Case dir: cluster_cases/<cluster>/<case>/. Holds case.svelte, optional config.json,
and per variant an expected (case-svelte.*) + actual (case-rust.*, never hand-edit)
snapshot pair — client (.js / .dev.js) and server (.server.js / .server.dev.js).
Each case pins four variants — client (prod / dev) and server (ssr / ssr_dev) —
so eight snapshot files. The #1 mistake is conflating expected (reference compiler) with
actual (ours):
| File | Variant | Written by | When |
|---|---|---|---|
case-svelte.js (expected) | client prod | just generate | step 5 |
case-svelte.dev.js (expected) | client dev | just generate | step 5 |
case-svelte.server.js (expected) | server prod | just generate | step 5 |
case-svelte.server.dev.js (expected) | server dev | just generate | step 5 |
case-rust.js (actual) | client prod | <fn>::prod (assert_compiler_prod, harness.rs) | step 7 |
case-rust.dev.js (actual) | client dev | <fn>::dev (assert_compiler_dev) | step 7 |
case-rust.server.js (actual) | server prod | <fn>::ssr (assert_compiler_ssr) | step 7 |
case-rust.server.dev.js (actual) | server dev | <fn>::ssr_dev (assert_compiler_ssr_dev) | step 7 |
compiler_case!(<fn>, "<path>") expands to four tests — <fn>::prod and <fn>::dev
(client, active) plus <fn>::ssr and <fn>::ssr_dev (server, #[ignore]d by default:
server parity is not yet reached project-wide). Each case-rust*.js is materialized only
once its test runs: just generate never writes them, and an unregistered or
#[ignore]d test never runs — so the two server actuals stay absent until you run them with
--include-ignored. This drives steps 5–7 and Red vs ignore below.
Pick path & cluster. Existing cluster (runes, legacy, each, events,
stores, await, const_tag, snippets, text_reactivity, component_props, …);
new one only if none fit. Stop if the dir exists (extend only if same feature and
case.svelte stays small).
Name the case (see Naming below). <case> snake_case, describing the
behavior/branch. Must-not-change branches end in _guard. No ad-hoc prefixes.
Write case.svelte — smallest component for one feature/edge. Reactive assertions
must mutate the $state (unmutated → const-folds → proves nothing); add a _guard
case for the must-not-change branch.
config.json if not default runes. Legacy: { "runes": false }. Mode MUST match
the feature. Keys (src/cases.rs): runes, dev, name, filename, namespace,
customElement, rootDir, preserveComments, preserveWhitespace, experimental.async.
just generate — writes the four expected reference snapshots only (case-svelte.js,
.dev.js, .server.js, .server.dev.js).
Register in clusters/<cluster>.rs — the step just generate does NOT do:
compiler_case!(<fn_name>, "<cluster>/<case>");. Divergence you are NOT fixing now → see
Red vs ignore below.
New cluster → create clusters/<cluster>.rs with use super::*; and add
#[path = "clusters/<cluster>.rs"] mod <cluster>; to test_clusters.rs.
just test-cluster <fn_name> — runs all four variants (incl. #[ignore], via
--include-ignored), writing the case-rust*.js actuals. For a client-only case only
::prod + ::dev gate — the server variants stay ignored/red (see Red vs ignore). Red is
normal test-first. Never edit snapshots to pass.
<case> is snake_case and names the behavior or Original branch it pins, never an
ordinal (case1, t_…) and never an ad-hoc prefix (g_… is banned — it is not a
convention, it appeared once by mistake)._guard — the one sanctioned marker. This is the codebase convention (muted_guard,
elem_unquoted_guard, …).elem_reactive / elem_unquoted_guard / elem_concat_text_guard.cluster_cases/<cluster>/<case>/ — never
several cases sharing one dir via filename tricks.cluster_cases/<cluster>/<group>/<case>/) only when a feature has real sub-axes;
the string passed to compiler_case! mirrors the directory path exactly.clusters/<first-path-segment>.rs (e.g. cases under
cluster_cases/attribute/single_expr/… register in clusters/attribute_single_expr.rs).An #[ignore]d test never runs, so it never materializes its case-rust*.js and never
checks parity (per the table above). The trap: just test-compiler runs cargo nextest run, which skips #[ignore]d tests by default, so ignored cases don't execute there;
only just test-cluster <fn> / just test-case <fn> (plain cargo test … -- --include-ignored) run them. The <fn> filter is a substring, so it matches all of
<fn>::prod / ::dev / ::ssr / ::ssr_dev.
ignore. The case is red; iterate with
just test-cluster <fn> (includes ignored, materializes case-rust.js +
case-rust.dev.js). When both client modes are green it joins the suite.ignore = "<reason>" — ignores ::prod and
::dev so the green suite (just test-compiler) stays green until you remove ignore.[prod, dev_todo] keeps ::prod in the suite and ignores just
::dev (or [prod] to drop dev entirely), so prod parity is enforced meanwhile.::ssr / ::ssr_dev as
#[ignore]d, so they never gate just test-compiler; under --include-ignored they run
and stay red until server codegen supports the construct — expected for a client-only case.
When you port SSR, flip to [prod, dev, ssr, ssr_dev] to enforce server parity (swap in
ssr_dev_todo if only server-prod is reached).