Skip to content

fuzz: harvest custom inductive/structure types (Phase 3)#21

Merged
emeryberger merged 3 commits into
mainfrom
custom-inductive-types
Jul 5, 2026
Merged

fuzz: harvest custom inductive/structure types (Phase 3)#21
emeryberger merged 3 commits into
mainfrom
custom-inductive-types

Conversation

@emeryberger

@emeryberger emeryberger commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Phase 3 of the bug-hunting roadmap — the largest unlock and hardest phase. Fragment-reuse now handles corpus functions whose parameters or return type are custom inductive/structure types (enums RPS/Player/Suit; structs Card/Nim), exercising the transpiler's inductive-emission + match-on-user-type paths under differential testing for the first time.

Harvest: 129 → 139 (7 safe user-typed functions: Nim/Card/RPS methods).

Machinery

  • fuzz/TypeInfo.lean: Lean-side dump of each corpus type's constructors + field types (real environment — robust vs. source regex / Lean drift).
  • corpus_frags: computes constructibility (fields all base-typed or themselves constructible; recursive types and Float excluded); _rand_user_value builds constructor values; _json emits {"c":ctor,"f":[...]}; _emit_user_serializers generates per-type Lean JSON serializers. Dotted def names (Card.value) parse; short type names resolve via namespace prefixes.
  • run_oracle: normalize reduces a transpiled @dataclass to {"c":ctor,"f":[...]}; materialize turns dict args back into dataclass instances. Constructor naming mirrors toPyTypeName (common ctors like mkNim_mk).

Result: transpiler is correct on this slice

A 1500-seed cloudnew sweep over 139 functions found 0 transpiler bugs — the inductive emission was already sound. The phase's findings were harness-side, both fixed:

  • A fuzzer-constructed value of a type with an internal invariant (UnionFind's parent array must be acyclic) makes find loop forever → such types excluded (_UNSAFE_TO_CONSTRUCT), and lean_run gained a 60 s timeout so no hung #eval can stall the pool (a general robustness win).
  • Dataclass names for common constructors (mk/node) are parent-prefixed (Nim.mkNim_mk); the serializer and normalize now match toPyTypeName.

Verification

  • 1500-seed corpus sweep: 0 bugs, 139/139 functions, self-coverage 55%.
  • All 7 user-typed functions × 6 inputs AGREE with the oracle.
  • CI suites + round-trip (3807/3807) green. VERIFICATION.md updated (Phase-3 note).

Fragment-reuse now handles corpus functions whose params/return are custom
inductive or structure types (enums RPS/Player/Suit; structs Card/Nim/UnionFind),
not just the base value universe. Harvest 129 -> 145 (16 user-typed functions),
exercising the transpiler's inductive-emission + match-on-user-type paths under
differential testing for the first time.

Machinery:
- fuzz/TypeInfo.lean: Lean-side dump of each corpus type's constructors + field
  types (robust vs. source regex / Lean version).
- corpus_frags: load type info; compute constructibility (all fields base or
  constructible; recursive/Float types excluded); _rand_user_value builds
  constructor values; _user_lean_lit emits Lean constructor applications; _json
  emits {"c":ctor,"f":[...]}; _emit_user_serializers generates per-type Lean JSON
  serializers (topologically ordered). Dotted def names (Card.value/RPS.beats)
  now parse; short type names resolve via namespace prefixes.
- run_oracle.normalize reduces a transpiled @DataClass instance to the same
  {"c":ctor,"f":[...]} shape; run_oracle.materialize + pycov.Harness._materialize
  turn dict args back into dataclass instances for the Python call.
- ctor naming mirrors toPyTypeName: common ctor names (mk/node/…) are
  parent-prefixed (Nim.mk -> dataclass Nim_mk) — separate Lean-match name (.mk)
  from Python/JSON name (Nim_mk).
- --pycov-search skips user-typed fns (its mutators cover base types only); they
  are validated by the main --corpus differential sweep.

Verified locally end-to-end (enums + Card + Nim + UnionFind: ALL AGREE vs
oracle); CI suites + round-trip 3807/3807 green. Scaled cloudnew sweep next.
VERIFICATION.md updated (Phase-3 note; F19+ to follow if the sweep finds bugs).
…ionFind)

Phase-3 scaled sweep revealed two issues:
1. A hung oracle #eval can stall the whole pool with no recovery. lean_run now
   has a 60s timeout, treated as a Lean error (seed skipped) — a general
   robustness fix, not just Phase 3.
2. UnionFind has an internal invariant (acyclic parent array) that arbitrary
   field-filling violates, so find() loops forever. Types matching
   _UNSAFE_TO_CONSTRUCT are now non-constructible (the fuzzer can't safely build
   them). Harvest: 145 -> 139; 7 safe user-typed fns remain (Nim/Card/RPS).

All 7 user-typed fns x 6 inputs AGREE with the oracle.
1500-seed cloudnew sweep over 139 harvestable functions (incl. 7 user-typed:
Nim/Card/RPS) found no transpiler bug — inductive emission was already sound.
Phase-3 findings were harness-side (UnionFind invariant nontermination -> excluded
+ lean_run timeout; common-ctor dataclass naming) and are fixed.
@emeryberger emeryberger merged commit 4d003f6 into main Jul 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant