Skip to content
Tórculo1.0 · working draft

Two implementations, the same cases

Conformance

The Tórculo conformance suite: the cases an implementation must pass to call itself conforming, and the latest report.

The suite every Tórculo implementation runs. A case is a small JSON file with an input and the exact expected output; an implementation conforms to a kind of case when it passes every non-pending case of that kind. Normative text: spec/SPEC.md. Suite version and case count: manifest.json. Changes: CHANGELOG.md.

Every pliego of the suite carries music and sound design (BANDA, EFECTOS, MEZCLA, MÚSICA, AMBIENTE, ESPACIO and sonido: effects). The sounds are synthesized by tools/sonido.py with the Python standard library and the canon's randomness: the same bytes on every machine, dedicated to the public domain (CC0 1.0). They are not committed; the generator and the runner make them (their SHA-256 are in the manifest).

Layout

PathWhatMade by
pliegos/*.mesathe pliegos: one per area of the language and per profilehand
pliegos/errores/*.mesaone broken rule per pliego, named after what it breakshand
pliegos/sonido/music cues (banda/) and effects and ambiences (efectos/)tools/sonido.py
manual/lee.jsonthe expected diagnostics of every pliego, written by handhand
expected/ir/*.ir.jsonthe frozen IR of the reference implementationtools/generar.py
expected/huella/*.jsonthe fingerprints (§13.4) and soundtrack reports of the reference interpretertools/generar.py --huellas
canon/vectores.jsonthe canon's test vectors, computed with independent integer arithmetictools/generar.py
laminas/*.dibujo.jsonthe drawings whose láminas every producer must reproduce byte for bytecopied from pluma/pruebas/referencia
tomas/*.take, tomas/fuentes/*.pytakes recorded by the engine fork, and the scenes that made themthe engine
tomas/invalidas/*.takeone broken rule per taketools/generar.py
mesapack/*.mesapacka minimal valid package and one broken rule per packagetools/generar.py
materias/*.wgslshaders that break the pass conventiontools/generar.py
cases/*.jsonone case per filetools/generar.py
informe.jsonthe last report of the runner against the reference implementationstools/ejecutar.py

The oracles

Independent readers written from the specification, Python standard library only:

ToolChecks
tools/jcs.pyRFC 8785 canonical JSON (self-test: python3 tools/jcs.py)
tools/ir.pythe normative projection of an IR (§10.6)
tools/toma.pytakes (§19); codes T001 to T007
tools/lamina.pyláminas (§17); codes L001 to L011
tools/mesapack.pypackages (§14); codes P001 to P009
tools/materias.pythe pass convention (§18); codes M001 to M006
tools/huella.pyfingerprints of a film (§13.4) and their tolerant comparison
tools/adaptadores/manim_saorin.pythe adapter to the reference implementation of the language

Regenerating and running

python3 conformance/tools/generar.py             # everything except the fingerprints
python3 conformance/tools/generar.py --huellas   # also renders the fingerprints (heavy)
python3 conformance/tools/ejecutar.py            # the quick classes (about a minute)
python3 conformance/tools/ejecutar.py --pesado   # everything: fingerprints, soundtrack, Rust láminas, package round trip

Requirements: Python 3.11 or later; Node 22 or later (TypeScript canon and the WebAssembly pen); rustc (Rust canon); ffmpeg (fingerprints); the Python with which manim-saorín runs (MESA_PY, by default the Homebrew Manim one) and its folder (MESA_IMPL); the pen's Python (TORCULO_PLUMA_PY). Heavy work goes through tools/pesado.

How expectations are made

So that nobody has to trust a single implementation:

  • Diagnostics are written by hand in manual/lee.json. The generator refuses to write a case if the reference disagrees, unless the case is marked pending with its reason.
  • Vocabularies are extracted from the parser (spec/vocabularios/extrae.py): the specification is that table, and the case checks the parser still says it.
  • IRs are the reference's, reviewed and frozen. Other compilers are held to the normative projection; the reference, to the whole IR.
  • Closure and mirror are properties: the runner computes both sides.
  • Canon vectors are computed in generar.py with integer arithmetic written again, not with the generated canon.
  • Láminas: their hashes come from the WebAssembly build of the pen (pluma/pruebas/referencia/_laminas.json); Python and Rust must give the same bytes.
  • Takes, packages, shaders: one broken file per rule, with the code the oracle must give; the generator stops if the oracle disagrees.
  • Fingerprints and soundtrack are the reference interpreter's, on the reference machine; they are compared with tolerance (§13.4) and the soundtrack against the targets of §8.2 (−14 LUFS ± 1 LU, true peak at most −1 dBTP, the cues and counts).

Case format

{"id": "lee-errores-afecto", "kind": "lee", "input": {"pliego": "pliegos/errores/afecto.mesa"},
 "expect": {"valido": false, "errores": ["E010"], "avisos": ["W113"]}}

id equals the file name; paths are relative to conformance/. A case MAY carry "pendiente": "reason": a known deviation of the reference implementation (SPEC Appendix D), reported apart.

kindinputexpectpasses when
vocabulariothe vocabularies fileigualesextracting from the parser gives the same file
leepliegovalido, errores, avisossame validity and the same sets of codes
irpliegoir (path), sha256, proyeccion_sha256the normative projection's fingerprint matches; for the reference, the whole IR too
cierrepliegoiguales_modulo_procedencia, se_quitacanon(compile(p)) == canon(compile(escribe(p)))
espejopliegoefectivo_igualthe effective workshop and hand of p, parte(p) and funde(parte(p)) are equal
canon_azarvectores, lenguaigualesthe language's canon gives every vector exactly
canon_paletavectores, lenguacoloresevery colour of the generated file matches canon.toml
laminadibujo, implementacionsha256, validathe packed lámina has that hash and validates
tomatomavalida, resumen or codigothe reader gives that summary, or rejects with that code
mesapackmesapackvalido, erroresthe validator gives exactly those codes
mesapack_referenciapliegovalido, erroresthe packager's package of that pliego validates
materiawgsl, modulosconforme, erroresthe checker gives exactly those codes
huellapliego, motor, calidadhuella, toleranciathe rendered film's fingerprint matches
bandapliego, motor, calidadloudness, true peak, cues, countsthe soundtrack report meets them

Adapters

The runner talks to an implementation through an adapter (tools/adaptadores/). An adapter for another compiler offers the same functions as manim_saorin.py: ir(pliego), lee(pliego) returning codes, escribe, parte, funde, taller, and, for an interpreter, toca_gx(pliego, out) writing the video, the master and the soundtrack report.

The report

ejecutar.py prints a one-line summary and writes informe.json: the suite version and cases_sha256, the date, the implementations, the totals (pasa, falla, pendiente, omitido), the totals per kind, and one result per case with its detail when it did not pass.

The latest report

torculo-conformance 0.2.0 · 15 October 2026 at 14:13 · 62.8 s

89 passes 0 fails 5 pending 8 skipped

What was tested

PartImplementation
lenguamanim-saorin · 1.0.0b1 · MESA v2 beta · 3f34801
canoncanon/gen (python, typescript, rust, wgsl, css)
laminaspluma (python, wasm, rust)
tomasconformance/tools/toma.py sobre tomas del fork
mesapackconformance/tools/mesapack.py y `mesa empaqueta`
materiasconformance/tools/materias.py sobre materias/shaders

By case class

Classpassesfailspendingskipped
banda2
canon_azar3
canon_paleta5
cierre31
espejo1
huella2
ir12
lamina63
lee313
materia10
mesapack10
mesapack_referencia1
toma8
vocabulario1
All 102 cases
CaseClassStatusDetail
banda-estampasbandaskippedclase pesada: con --pesado
banda-oficiobandaskippedclase pesada: con --pesado
canon-azar-pythoncanon_azarpasses
canon-azar-rustcanon_azarpasses
canon-azar-typescriptcanon_azarpasses
canon-paleta-csscanon_paletapasses
canon-paleta-pythoncanon_paletapasses
canon-paleta-rustcanon_paletapasses
canon-paleta-typescriptcanon_paletapasses
canon-paleta-wgslcanon_paletapasses
cierre-dirigidocierrepasses
cierre-escenacierrepasses
cierre-servilletacierrependingLa referencia rompe el cierre en una servilleta con banda: `mesa escribe` reescribe el preámbulo y pierde BANDA, EFECTOS, MEZCLA, MÚSICA y AMBIENTE (la IR escrita ya no suena). [/banda: sonido/banda ≠ None; /beats[0]/aspecto/ambiente: amb sala bucle ≠ None; /beats[0]/aspecto/musica: 1M1 ≠ None; /beats[1]/aspecto/ambiente: amb sala bucle ≠ None; /beats[1]/aspecto/musica: 1M1 ≠ None; /beats[2]/aspecto/ambiente: amb sala bucle ≠ None; /beats[2]/aspecto/musica: 1M1 ≠ None; /beats[3]/aspecto/ambiente: amb sala bucle ≠ None]
cierre-tallercierrepasses
espejo-tallerespejopendingLa referencia rompe la ley del espejo con la MANO de este pliego: importada desde `manos/firme.mano`, `toca la truca: sí` se lee como `no`; escrita en línea, `precisión alta` queda como una clave sin normalizar al lado de `precision alta`; y `parte` pierde el nombre de la sección (`#TALLER collage` → `#TALLER`). [MANO efectivo distinto: toca la truca: no; toca la truca: si]
huella-estampashuellaskippedclase pesada: con --pesado
huella-oficiohuellaskippedclase pesada: con --pesado
ir-aspectoirpasses
ir-bandairpasses
ir-camarairpasses
ir-cartelirpasses
ir-dirigidoirpasses
ir-escenairpasses
ir-estampasirpasses
ir-oficioirpasses
ir-oidoirpasses
ir-raccordirpasses
ir-servilletairpasses
ir-tallerirpasses
lamina-manicula-pythonlaminapasses
lamina-manicula-rustlaminaskippedRust: con --pesado
lamina-manicula-wasmlaminapasses
lamina-nota-aqui-pythonlaminapasses
lamina-nota-aqui-rustlaminaskippedRust: con --pesado
lamina-nota-aqui-wasmlaminapasses
lamina-tintero-pythonlaminapasses
lamina-tintero-rustlaminaskippedRust: con --pesado
lamina-tintero-wasmlaminapasses
lee-aspectoleepasses
lee-bandaleepasses
lee-camaraleepasses
lee-cartelleepasses
lee-dirigidoleepasses
lee-errores-accionleepasses
lee-errores-afectoleepasses
lee-errores-ambienteleepasses
lee-errores-ambiguedadleepasses
lee-errores-anclaleepasses
lee-errores-bestiario-ausenteleepasses
lee-errores-collageleepasses
lee-errores-color-vozleepasses
lee-errores-cueleepasses
lee-errores-dibujoleepasses
lee-errores-efectoleepasses
lee-errores-epocaleepasses
lee-errores-espacioleepasses
lee-errores-eventoleepasses
lee-errores-funcionleepasses
lee-errores-gritosleepasses
lee-errores-letra-preambuloleependingLa referencia acepta `LETRA` (una línea de clip) en el preámbulo sin error y la descarta en silencio: el signo no llega a la IR. Por la ley de las tres sedes (§3) una línea fuera de su sitio es un error (E090), nunca una línea que desaparece. [esperado {'avisos': ['W113'], 'errores': ['E090'], 'valido': False}, da {'valido': True, 'errores': [], 'avisos': ['W113', 'W124']}; mensajes: ['sin «silencio final» declarado (se aplica 3)', 'línea 12 · página vacía · b01: el beat corta a un cuadro nuevo y no po']]
lee-errores-letreroleepasses
lee-errores-movimientoleepasses
lee-errores-pantallaleepasses
lee-errores-pieza-sin-repartoleepasses
lee-errores-reservadaleependingLa referencia acepta `REPARTO nota: …` sin error y luego trata `nota:` como directiva (avisa «clip vacío»). §5.5 y §6.4 exigen E021 al declarar un hablante con una palabra reservada. [esperado {'avisos': ['W113'], 'errores': ['E021'], 'valido': False}, da {'valido': True, 'errores': [], 'avisos': ['W113', 'W116']}; mensajes: ['línea 12 · b01: clip vacío', 'sin «silencio final» declarado (se aplica 3)']]
lee-escenaleepasses
lee-estampasleependingLa referencia avisa «clip vacío» (W116) en un clip cuyo único contenido es un signo (`LETRA`). Según §8.3 un signo es una pieza y el clip no está vacío. [esperado {'avisos': ['W112'], 'errores': [], 'valido': True}, da {'valido': True, 'errores': [], 'avisos': ['W112', 'W116']}; mensajes: ['porcentaje pobre 82 % (sano: ≥85 %). 7 líneas con cifras; el pliego di', 'línea 39 · b03: clip vacío']]
lee-oficioleepasses
lee-oidoleepasses
lee-raccordleepasses
lee-servilletaleepasses
lee-tallerleepasses
materia-acuarelamateriapasses
materia-difuminadomateriapasses
materia-papelmateriapasses
materia-pincelmateriapasses
materia-risografiamateriapasses
materia-rota-m001materiapasses
materia-rota-m004materiapasses
materia-rota-m005materiapasses
materia-tejidomateriapasses
materia-transicionmateriapasses
mesapack-formato-futuromesapackpasses
mesapack-hash-rotomesapackpasses
mesapack-ida-y-vuelta-dirigidomesapack_referenciaskippedclase pesada: con --pesado
mesapack-minimomesapackpasses
mesapack-no-zipmesapackpasses
mesapack-ruta-peligrosamesapackpasses
mesapack-sidecar-absolutomesapackpasses
mesapack-sin-declararmesapackpasses
mesapack-sin-manifiestomesapackpasses
mesapack-sin-pliegomesapackpasses
mesapack-sin-versionesmesapackpasses
toma-banotomapasses
toma-estampatomapasses
toma-invalida-codigo-desconocidotomapasses
toma-invalida-cortadatomapasses
toma-invalida-post-fuera-de-sitiotomapasses
toma-invalida-sin-starttomapasses
toma-invalida-tras-endtomapasses
toma-invalida-version-upstreamtomapasses
vocabulario-parservocabulariopasses

informe.json