Found by wiring examples/ and conformance/ into CI for the first time (scripts/check-corpus.sh). Both directories existed but were executed by nothing — not CI, not the Justfile.
The cost of that was concrete: examples/isotopy.tangle — the file whose whole purpose is to demonstrate the language's central claim — was failing its own assertions, and had been for as long as ~ used free reduction. It only started passing when TG-7 (#87) made ~ decide real braid-group equivalence. Verified by building the pre-TG-7 commit and running it: Runtime error: Assertion failed.
The gate is a ratchet: current state is locked in, nothing may regress, nothing may join the known-bad lists, and anything that starts working must be removed from them (a stale entry fails the build). This issue tracks emptying those lists.
A. Examples that parse but do not typecheck (5 of 7)
| File |
Cause |
trefoil.tangle, braids_as_data.tangle |
Recursive function returning Num. def length(w) = match w with | identity => 0 | s1 . rest => 1 + length(rest) ... — the recursive call infers as Word[0], so 1 + length(rest) is rejected: "Cannot add Num and Word[0]". The typechecker has no recursive-binding rule, so a function's own return type isn't available while checking its body. |
compositional_pd.tangle, tensor_product.tangle |
identity == braid[...] is a width mismatch. identity : Word[0] vs Word[n], and T-Eq-Word requires equal widths: "Cannot compare words of differing width: Word[0] == Word[3]". Note the Lean model has dedicated eqIdBraid / eqBraidId step rules for exactly this shape, so the semantics anticipates a comparison the typechecker forbids — worth reconciling deliberately rather than by accident. |
turing_complete.tangle |
Match arms disagree in width — Word[2] vs Word[0]. Arms must agree, and there is no width subsumption/widening at the match join. |
These are genuine language gaps, not broken files. Each is a decision:
- Recursive typing — add a recursive binding rule (bind the function's declared/inferred type in its own body). Biggest of the three, and the one that unlocks the most idiomatic Tangle.
- Identity/word comparison — either widen
T-Eq-Word so Word[0] (identity) is comparable with any Word[n], or make identity polymorphic in width. The Lean side already models it; this is mostly a decision about which.
- Match-arm width join — decide whether arms may differ in width and what the join is.
B. conformance/valid/ programs the parser rejects (5 of 19)
| File |
Unimplemented surface |
v02_weave, v08_crossing, v09_twist, v12_weave_expr |
weave strands a:Q, b:Q into (...) yield strands b:Q, a:Q — typed strands with a yield strands clause. The parser accepts a simpler weave form; these use the fuller spec syntax. |
v11_add_block |
add{ 1 + 2 + 3 } — the add{} block (JTV data injection). Not in parser.mly at all. |
These are spec-vs-implementation divergences: the conformance corpus encodes surface syntax the parser does not implement. Either the parser catches up, or the spec/corpus is corrected to match the intended language — but the divergence should not sit silent.
C. Incidental: the conformance runner never worked
conformance/run_conformance.sh invoked dune exec -- tangle --parse-only. Both halves were wrong: the executable is main (see compiler/bin/dune), not tangle, and there has never been a --parse-only flag. So every invocation failed, and the suite reported 3/19 — where all three "passes" were invalid/ cases, which "pass" precisely when the command fails. It failed for the wrong reason and scored points for it. Fixed in the same PR; the real number is 14/19.
Suggested order: B is mechanical (parser work, well-bounded). A2 and A3 are small typing decisions. A1 (recursive typing) is the substantial one and the most valuable — it is what makes length-style programs expressible, which is most of what the examples are trying to show.
Found by wiring
examples/andconformance/into CI for the first time (scripts/check-corpus.sh). Both directories existed but were executed by nothing — not CI, not the Justfile.The cost of that was concrete:
examples/isotopy.tangle— the file whose whole purpose is to demonstrate the language's central claim — was failing its own assertions, and had been for as long as~used free reduction. It only started passing when TG-7 (#87) made~decide real braid-group equivalence. Verified by building the pre-TG-7 commit and running it:Runtime error: Assertion failed.The gate is a ratchet: current state is locked in, nothing may regress, nothing may join the known-bad lists, and anything that starts working must be removed from them (a stale entry fails the build). This issue tracks emptying those lists.
A. Examples that parse but do not typecheck (5 of 7)
trefoil.tangle,braids_as_data.tangleNum.def length(w) = match w with | identity => 0 | s1 . rest => 1 + length(rest) ...— the recursive call infers asWord[0], so1 + length(rest)is rejected: "Cannot add Num and Word[0]". The typechecker has no recursive-binding rule, so a function's own return type isn't available while checking its body.compositional_pd.tangle,tensor_product.tangleidentity == braid[...]is a width mismatch.identity : Word[0]vsWord[n], andT-Eq-Wordrequires equal widths: "Cannot compare words of differing width: Word[0] == Word[3]". Note the Lean model has dedicatedeqIdBraid/eqBraidIdstep rules for exactly this shape, so the semantics anticipates a comparison the typechecker forbids — worth reconciling deliberately rather than by accident.turing_complete.tangleWord[2]vsWord[0]. Arms must agree, and there is no width subsumption/widening at the match join.These are genuine language gaps, not broken files. Each is a decision:
T-Eq-WordsoWord[0](identity) is comparable with anyWord[n], or make identity polymorphic in width. The Lean side already models it; this is mostly a decision about which.B.
conformance/valid/programs the parser rejects (5 of 19)v02_weave,v08_crossing,v09_twist,v12_weave_exprweave strands a:Q, b:Q into (...) yield strands b:Q, a:Q— typed strands with ayield strandsclause. The parser accepts a simplerweaveform; these use the fuller spec syntax.v11_add_blockadd{ 1 + 2 + 3 }— theadd{}block (JTV data injection). Not inparser.mlyat all.These are spec-vs-implementation divergences: the conformance corpus encodes surface syntax the parser does not implement. Either the parser catches up, or the spec/corpus is corrected to match the intended language — but the divergence should not sit silent.
C. Incidental: the conformance runner never worked
conformance/run_conformance.shinvokeddune exec -- tangle --parse-only. Both halves were wrong: the executable ismain(seecompiler/bin/dune), nottangle, and there has never been a--parse-onlyflag. So every invocation failed, and the suite reported 3/19 — where all three "passes" wereinvalid/cases, which "pass" precisely when the command fails. It failed for the wrong reason and scored points for it. Fixed in the same PR; the real number is 14/19.Suggested order: B is mechanical (parser work, well-bounded). A2 and A3 are small typing decisions. A1 (recursive typing) is the substantial one and the most valuable — it is what makes
length-style programs expressible, which is most of what the examples are trying to show.