RFC-0030: Fuzz infrastructure catch-up — multi-RFC corpus migration
- Status: Accepted (Execution committed 2026-05-18)
- Author: Mark Truluck mark.truluck@cogiton.com
- Created: 2026-05-18
- Prerequisite to: Frame 4.1.2 RC (or whichever release this lands in)
Framing. RFC-0029 catalogued the fuzz infrastructure status and recommended treating it as developer-side preventative infra not blocking RC. The 2026-05-18 pre-RC validation pass exposed that the fuzz corpus has accumulated multi-RFC staleness over months — the persist generator emits a contract three RFC amendments behind (RFC-0012 + RFC-0015), per-language renderers in
langs.pybake the old persist API across 40+ sites, and multiple shell-phase generators emit drivers that bypass the RFC-0015 factory contract. The matrix has been the working release signal during this period; the fuzz harness silently rotted. This RFC is the catch-up plan. Estimated 1-2 weeks focused work; explicitly scoped to RC-block until done.
What “stale” actually means here
The fuzz harness has two layers:
- Generators (
fuzz/diff_harness/gen_*_pure.pyfor Phase 2-7;fuzz/gen_*.pyfor Phase 8-21) — produce abstract Frame source per case. Several emit pre-RFC syntax. - Per-language renderers (
fuzz/diff_harness/langs.py, 4001 LOC) — render each Frame case into language-specific runnable programs (instantiate, drive, save/restore, classify trace). Many renderers call the old persist API.
Both layers need updating. The 2026-05-18 pre-RC investigation confirmed three concrete staleness classes:
Class 1: RFC-0013 — @@target lang → @@[target("lang")]
The hard cut shipped in framec via E804. All 7 diff-harness
generators emit the bare form, but run_fuzz.py rewrites it to
the attribute form at runtime, so this class is functionally
masked — generators are stale but the harness compensates.
Recommend cleaning the generators anyway (consistency, removes
the rewrite shim).
Class 2: RFC-0012 + RFC-0015 — persist contract
Two amendments compounded:
- RFC-0012 amendment:
@@[persist]alone is insufficient; must declare@@[save]/@@[load]. - RFC-0015 hard-cut:
operations:block form deprecated. Module-level attributes mandatory:@@[persist(<type>)] @@[save(<method-name>)] @@[load(<method-name>)] @@system Foo { ... }Plus the API shift:
Foo.restore_state(blob)classmethod →inst.restore_state(blob)instance method (caller creates the instance via factory).
Impact:
gen_persist_pure.pyemits the pre-amendment form (@@persistbare).langs.pycallsFoo.restore_state(blob)in 40+ places across every backend’s persist renderer.
Class 3: RFC-0015 factory bypass in shell-phase drivers
Shell-phase generators (gen_perm.py, gen_ctrl_flow.py, etc.)
embed a per-language driver that does _inst = Foo() (bare
constructor). RFC-0015 made the factory the only valid
construction form — Foo() skips the $> dispatch and
state-vars never get populated.
Surfaced as 3 cases in Phase 10 (perm) + 12 (ctrl_flow) failing
with KeyError: 'scache' at runtime when handler bodies read
state-vars from an uninitialized compartment.
Likely affects all shell-phase generators with per-language driver templates — needs survey.
Execution plan
Six discrete tracks, sequential or parallel as the surface allows. Estimated effort assumes one focused worker; explicitly not the developer-side smoke-tier work that already runs in 2 minutes.
Track A — Persist phase end-to-end (4-8 hr)
- Update
gen_persist_pure.pyto emit RFC-0015 contract (@@[persist(<type>)]+ module-level@@[save]/@@[load]). ✅ Generator emit updated 2026-05-18. - Update
langs.pypersist renderer for all 17 backends:Foo.restore_state(blob)→ factory-construct + instance call. The persist-blob type is per-target (Pythonstr, RustString, GDScriptPackedByteArray, etc.); add a per-Langfield for it. - Regenerate
cases/persist/with new generator. - Run
python3 diff_harness/run_fuzz.py --cases cases/persist --langs <each>per backend; verify 17/17 pass-or-skip green. - Land Track A’s commits as a single PR-ish unit; clean handoff point.
Track B — Multisys phase (~1 hr)
- Update
gen_multisys_pure.pyto emit@@[main]on the lead system (RFC-0021 requirement for multi-system modules). - Regenerate
cases/multisys/. - Verify diff-harness pass.
Track C — Shell-phase driver factory migration (3-6 hr)
- Audit all
gen_*.pyshell-phase generators for_inst = Foo()pattern in driver templates. - Replace with the per-language factory form. Per the
cases_persist_x/reference: Frame-level@@SystemName()syntax (compiled to per-backend factory automatically) is the cleanest path — works in any backend without per-language driver branches. - Regenerate affected
cases_*directories. - Verify state-var-using cases now compile + run clean.
Track D — Other-phase renderer survey (4-8 hr)
langs.py is 4001 LOC of per-backend renderers. Tracks A-C
address the known issues; this track is the systematic survey:
for each phase (selfcall, hsm, operations, async, multisys,
nested, perm, etc.), verify the per-backend renderer matches
current Frame API. Specifically check:
- Construction (factory vs bare ctor)
- Dispatch (instance method invocation shape)
- Trace emission (matches
TRACE_FORMAT.mdv1) - Persist (if applicable — only persist + persist_x phases use it)
Open dedicated sub-tasks for each renderer divergence found.
Track E — Regen all 35k cases (~30 min)
After Tracks A-D land, regenerate the full corpus:
cd fuzz/
for gen in diff_harness/gen_*_pure.py gen_*.py; do
python3 "$gen"
done
Spot-check the per-phase counts match FUZZ_PLAN.md expectations.
Track F — Full fuzz re-run + surfaced-defect fixing (open-ended)
FRAMEC=... ./run_all.sh --tier=full --lang=all should now run
all phases. Real framec defects that surface — fix in framec
(separate commits) and re-run. Estimated yield based on historical
ratio: 3-8 defects over the corpus regeneration. Each defect adds
~30-60 min to investigate + fix + verify, plus matrix re-run for
regression confirmation.
Tracking
Each track lands as its own commit (or commit cluster). After all
six green, run_all.sh --tier=full --lang=all should report
every phase PASS — that’s the RC bar for fuzz coverage.
Roadmap: #172 was closed by RFC-0029 as “the work was done.” This
RFC reopens that question — the work was built but the corpus
- renderers have rotted such that “the fuzz works” is no longer true. Closing this RFC = the fuzz works again.
Drawbacks
- 1-2 weeks blocks the RC. Real time cost. Acceptable per Mark’s 2026-05-18 decision; documented here so the cost is visible.
- The matrix has been the working RC signal during the rot period. Fixing fuzz doesn’t catch issues that have already shipped; it restores the ability to catch future regressions that the matrix misses by construction (the matrix is curated; fuzz is generated permutation).
- Renderer migration is mechanical but error-prone. 40+ per-
language sites in
langs.py; each migration must preserve trace-line semantics. Risk of introducing rendering bugs that produce false-positive divergences. Mitigation: run each track’s cases through the diff-harness BEFORE moving to the next; bisect any divergence to the renderer change. - No proactive guard for future RFC rot. Without a “matrix + fuzz must both pass before merge” CI policy, the corpus will rot again as soon as the next breaking RFC ships. Separate concern, separate RFC (cf. RFC-0029 § CI integration question).
Unresolved questions
-
Should the per-language renderer migration be one big commit or 17 small commits? Big-bang is easier to review for the pattern (factory + instance call); per-lang is easier to bisect if a divergence surfaces. Lean per-lang.
-
run_fuzz.py’s@@targetrewrite shim — keep or remove? Cleaning Track A’s generator emission means the shim is no longer load-bearing. Removing it would catch future generator staleness immediately rather than silently masking it. Recommend remove as part of Track A close-out. -
Smoke tier vs full tier as the RC bar. Smoke is ~2 min; full is ~45 min. Recommend smoke for ongoing pre-PR gate, full for release candidates only. Decide in RFC-0029 § CI integration follow-up.
References
- RFC-0029 — fuzz infrastructure status pre-this-RFC
framec-test-env/fuzz/FUZZ_PLAN.md— phase catalogframec-test-env/fuzz/diff_harness/langs.py— 4001-LOC renderer file; primary surface for Tracks A and Dframec-test-env/fuzz/diff_harness/gen_persist_pure.py— Track A generator (already updated 2026-05-18)- Roadmap #172 (closed by RFC-0029, effectively reopened by this)