You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Track and prioritize the remaining open JuLC work after PR #109 merged ADR-032 into main at 4401b1a8.
This is a planning and sequencing umbrella. The 0.1.0-pre17 label identifies the planning horizon; it does not make every deferred research item a release blocker. Correctness, deterministic evaluation, and dependency order take precedence over feature count.
Status reconciliation — 2026-09-21
P4 waves 1–4 are merged: #141, #142, #144, #147, #148, #149 and #150. #112/#114/#115/#116/#117/#119/#120 are implemented within their documented bounded scopes; #113 is complete as a rejected proposal, not an implementation. PR #156 subsequently removed the compiler's pinned-cost-profile requirement without changing O9 lowering.
Correctness PRs #157, #164, #165 and #167 are also merged. PR #167 merged on 2026-09-21 at 539c3f15, automatically closing #166 as completed. The loop/switch fixes tracked here are landed. Keep this tracker open: the profile-freeze ADR/hash-stability policy, final evidence audit and release-candidate validation/publication are not established as complete. GitHub still lists pre16 as the latest release.
The per-slice measurements below are historical implementation evidence, not a fresh audit of one final release artifact. Scope exclusions and residual research are not completed merely because an implementation PR merged.
Outer-loop/switch-arm validation follow-up — merged PR Fix switch-arm validation inside outer loops (stacked on #164) #165 (2026-09-21): check the selector under outer-loop restrictions; keep arm ownership and actual nested-loop checks. Arm-local guarded loops can yield results inside an outer loop.
#40 is not a prerequisite for #74 or a pre17 release blocker. New evaluation work should still avoid introducing additional shared mutable configuration.
P2 — non-blocking hardening, cleanup, and fidelity work
Make JulcTransactionEvaluator thread-safe (currently requires one instance per thread) #40 — non-blocking concurrency hardening for applications that share one JulcTransactionEvaluator. Current Yano main serializes the REST evaluation path and creates a fresh JuLC evaluator per invocation, so its endpoint does not expose the race. Keep the public issue open for broader API hardening.
Each slice must preserve evaluation order, decode/failure timing, representation boundaries, deterministic bytes, source attribution, and cross-backend behavior. Do not replace these with untyped UPLC peepholes.
P4 — PV11 lowering completion (scope decision 2026-09-13: do before the remaining release gates)
Scope decision (maintainer, 2026-09-13). All P4 items move ahead of the evidence-audit and release-candidate gates. Rationale: every one of them lowers to PV11-only builtins under PV11_SAFE/PV11_COSTED and changes emitted bytes/hashes for eligible programs; pre17 is a preview where that churn is acceptable, and landing it now lets the next milestone freeze the default PV11 rule set so later releases change PV11 hashes only for announced correctness fixes. NONE/BASELINE retain historical bytes for these optimization slices; that is not a blanket guarantee for subsequent correctness fixes such as #161/#166, whose affected-script changes apply at all levels. Each slice follows the #110/#111 pattern: ADR → bounded implementation → semantic/failure/deterministic-hash/size-budget/Java+Truffle+Scalus evidence → independent review → node evidence where material → release-note migration line → Blaster fixture check (verification/blaster/fixtures).
Original execution order (2026-09-13; completion status below):
Then: profile-freeze ADR (default PV11_SAFE rule set frozen; new rules only in a new opt-in profile) and the hash-stability policy doc, followed by the evidence audit, release-candidate validation, and the pre17 publish.
ADR-032 follow-up O8: implement typed native Value conversion motion #114 — wave 2: merged PR feat(compiler): share repeated native Value conversions at the safe profile (ADR-042, O8) #142 (2026-09-15, ADR-042); issue closed. Strict-prefix sharing of a repeated unValueData conversion of one variable at PV11_SAFE (pv11.o8.value-sharing): shared only when the conversion is already the first non-trivial evaluation of its scope, so results, traces and failure text are unchanged; per-iteration loop conversions hoist (−1.5M CPU on a 3-iteration fixture); loss bounded at 48,000 CPU per binding on paths that never reach a second occurrence; ValueData sinking/cancellation and Data-side ValuesLib sharing recorded as out of scope; no shipped example changes hash; goldens from 5e32bbcd.
ADR-032 follow-up O9: implement costed List-to-Array promotion gate #115 — wave 2: merged PR feat(compiler): promote repeatedly indexed lists to PV11 arrays at the costed profile (ADR-043, O9, #115) #144 (2026-09-15, ADR-043); issue closed. Costed-only list-to-array promotion (pv11.o9.list-to-array, fires only at PV11_COSTED): a JulcList variable indexed at two or more get sites, or at a site inside a loop/recursive helper/inlined callback, is converted once with ListToArray and its sites become IndexArray, bound at the innermost evaluated-once sub-term (two-site output byte-identical to a hand-written toArray()). Failure contract: out-of-range index fails at IndexArray (same point, smaller budget, different off-chain text), observable from validators through the strict boundary, pinned on Java/Truffle/Scalus. Break-even measured from the pinned profile and reproduced exactly (two sites at 0/1 gain up to 44 elements; WingRiders-shaped loop with 16 requests 349.6M → 81.9M CPU). Only bindings proven to hold a list are promoted (proven-name environment; casts and every alias of them, including the loop lowering's let xs = xs, are never promoted; callback parameters never trusted); the residual through helper parameters/returns and loop state is pinned as a documented costed-only divergence (CAST_HELPER, CAST_LOOP_STATE) — retained as the merged costed-only contract, not a claim that unchecked casts preserve safe-profile behavior. DropListget lowering measured and filed as Decide: lower JulcList.get(i) to a guarded DropList form instead of the recursive traversal (measured under ADR-043 / #115) #143. Goldens from 940dc65b (20 fixtures × 4 levels); default level byte-identical everywhere; external examples at pv11-costed byte-identical (no promotable shape in the compiled corpus; the real WingRiders pool validator compiled with the CLI drops 1,943 → 1,699 bytes).
ADR-032 follow-up O10: design typed native Array literals and safe folds #116 — wave 4: merged PR feat(compiler): typed array literals and literal folding at the safe profile (ADR-046, O10) #149 (2026-09-16, ADR-046); issue closed.JulcArray.of(a, b, ...) is the typed array literal: lowered to ListToArray over the JulcList.of list literal, with the Data-encoded element representation JulcArray<T> already has (a native Value element is JULC0041); PV11 only. ArraySemantics in julc-core pins the three array builtins (both IndexArray failure texts) and the VM delegates to it. ArrayLiteralFoldPass (rule pv11.o10.array-literal-fold, PV11_SAFE, switchable; the ADR-045 machinery is now the abstract LiteralFoldPass) folds ListToArray of a list literal to an array constant, LengthOfArray and literal-index IndexArray over it to their results, and the get decode only over elements it produced (never a pre-existing Data constant); out-of-range literal indexes stay and fail with the builtin's text; MultiIndexArray is never emitted. Additive: no existing program contains an array constant or converts a list literal to an array; every golden suite, the Blaster lock and all 41 example validators are byte-identical at every level. 19 fixtures × 4 levels × rule off/on on three VMs; e.g. JulcArray.of(1, 2, 3).get(1) 42 → 6 bytes, 1,238,594 → 16,100 CPU; a runtime-indexed table 49 → 34 bytes, 1,435,338 → 625,598 CPU on every path. Two review agents, no blocking finding (fixes: objective on the Bool fold, requirement key under the qualified class name, stronger failure/budget assertions, var divergence pinned). Open: a native array integer representation behind its own marker type, sharing repeated length/access on one array, map elements and negated literals not read as literals, on-chain gate for an embedded array constant.
ADR-032 follow-up O11: define typed BLS values and implement explicit MSM #117 — wave 4: merged PR feat: typed BLS12-381 values, native scalar/point lists and explicit multi-scalar multiplication (ADR-047, O11, #117) #150 (2026-09-17, ADR-047); issue closed. Typed BLS12-381 surface: JulcG1/JulcG2/JulcMlResult and the native lists JulcScalars/JulcG1Points/JulcG2Points (opaque PIR types under the O7 isolation rules, JULC0041/JULC0042); every Builtins.bls12_381_* and BlsLib method retyped (compress the only way to bytes, uncompress the only way back); the registry's per-builtin signature table rejects a wrong group, a byte string, Data or a Data list at compile time. Explicit g1MultiScalarMul/g2MultiScalarMul over native lists built only by the intrinsics scalars/g1Points/g2Points and the converters scalarsFromList/g1PointsFromCompressed/g2PointsFromCompressed (no Data wrapper possible); MSM semantics pinned on three VMs (all scalars validated first at 512 bytes, zip to the shorter list, empty sum is the identity). No pass, no fusion. Additive: no corpus/golden/Blaster program uses the BLS surface; a var-style BLS program compiles to identical bytes under both APIs; all 41 example validators byte-identical at every level. Crossover on the pinned profile: MSM smaller from three points, cheaper in CPU from seven (net of the shared point hashing: about 322M to enter + 25M per point versus 77M per point). Review fixes: element typing of scalars and the converters; same-class helper arguments and return expressions now under the native check. Migration: byte[] locals of BLS values are now JULC0041 (var unaffected). Open: literal-scalar bound check, chain fusion (costed), inverse converter, on-chain gate.
ADR-032 follow-up O14: define native Value literals and safe folding #119 — wave 4: merged PR feat(compiler): typed native Value literals and literal folding at the safe profile (ADR-045, O14) #148 (2026-09-16, ADR-045); issue closed. Typed native Value literal producers as Builtins intrinsics (emptyValue() = a UPLC Value constant; singletonValue(policyId, tokenName, quantity) and lovelaceValue(quantity) = insertCoin into it, with the builtin's zero/negative/key/range rules), gated by the PV11 Value-constant capability. NativeValueSemantics in julc-core pins the seven Value builtins (exact failure texts) and the VM now delegates to it, so the compiler fold and the runtime share one implementation. ValueLiteralFoldPass (rule pv11.o14.value-literal-fold, PV11_SAFE, switchable) folds a saturated all-literal call of any Value builtin (bare, through a NativeValueLib wrapper, or over once-bound literal locals) into its result when the semantics succeed and the literal's FLAT encoding is not larger than the call's; rejected calls stay as written (same failure text on Java/Truffle/Scalus); no algebraic identities. Additive: no existing program contains a Value literal, so every golden suite, the Blaster lock and all 41 example validators are byte-identical at every level (the producers are intrinsics precisely so that no dead library binding is added). 29 fixtures × 4 levels × rule off/on on three VMs; e.g. a literal lookup 40 → 7 bytes, 883,863 → 16,100 CPU. Two review agents found one blocking hole (a wrapper with an unused parameter dropped that argument unexamined), fixed with probes; the objective is measured in FLAT bits against the call site. Open: CPU-aware objective for fromData of large Data literals, toData(emptyValue()) stays a call by two bytes, negated literals (new BigInteger("-5")), folding the empty-Value idiom (hash-moving), NativeValueLib spelling once bindings are pruned, on-chain gate for an embedded Value constant.
ADR-032 follow-up O15: add minimal representation-aware let sharing #120 — wave 3: merged PR feat(compiler): share repeated record field projections at the safe profile (ADR-044, O15) #147 (2026-09-15, ADR-044); issue closed. The ADR-042 sharing pass generalised to three unit classes under the same leading rule: record field projection chains (D(headList(tailList^k(sndPair(unConstrData(x))))), one wrapDecode arm, matched outermost-first, a matched unit a leaf for collector and rewriter alike) and the fields prefix (sndPair(unConstrData(x))) join the native Value conversion; rounds run chains to a fixed point (chain on chain), then the prefix, then Value; units inside transitively dead bindings (uncalled library methods) are skipped; the body of a single recursive binding of a lambda can lead (the per-site get lowering). Rule pv11.o15.projection-sharing at PV11_SAFE, failure-text neutral on Java/Truffle/Scalus; exact cost model pinned (after = before + (2+k)·16,000 − (k−1)·unit; loss bound 48,000 CPU per binding). Each PIR rule is now individually switchable (CompilerOptions.disableOptimizationRule, JULC0043 for unknown ids), as ADR-032 follow-up O15: add minimal representation-aware let sharing #120 requires. Hashes move at the default level: 26 of 41 example validators change hash or size (the rule's trigger shape is in most validators); the hash-stability policy doc gets its precedent here. Goldens from 1fd98c93 (24 fixtures × 4 levels, self-verifying with the rule off); O8 fixtures byte-identical with every rule on; costed-only handoff: O9 now promotes x.items().get(i) sites because the list is bound first. Two review agents + advisor, all should-fixes folded. Open: units that do not lead, ValuesLib repeats, provenance from the final artifact, compareTo receiver duplication (compareTo lowering evaluates a non-trivial receiver and argument twice #146), loop self-alias, plugin/CLI switch exposure.
Post-implementation configuration correction:
Simplify cost-profile naming and require profiles only where cost parameters are used #153 — merged PR Decouple compiler optimization from pinned cost profiles #156 (2026-09-20): pv11-costed compiles without a pinned compiler cost profile. O9 eligibility is structural, not a runtime numeric profitability gate; pinned cost models remain benchmark/evaluation inputs. The neutral plutus-v3-pv11-costs-v1 ID preserves the original parameters and hash, and the old ID remains a compatibility alias. Compiler provenance no longer attributes an unused cost profile. This does not constitute the still-pending profile freeze/hash-stability policy.
Typed PIR ListMatch lowers eligible for-each traversals to guarded PV11 List Case. The original NullList guard preserves malformed-runtime-value failures and element decoding remains strict.
Enabled in default PV11_SAFE and in PV11_COSTED for the explicit PV11 target. NONE/BASELINE retain historical bytes. Recompilation with the updated safe profile can change script bytes/hashes; deployed scripts and ledger Data encodings are unchanged.
Independent Claude review reported approval with notes. Follow-ups restore decompiler loop classification, add explicit serialized List Case branch-order/binding/laziness/failure tests on Java/Truffle and applicable Scalus, and reconcile ADR-032/034 bookkeeping.
Focused tests, affected modules, full repository build and documentation build passed locally. Follow-up suites passed all 96 decompiler and 21 benchmark tests. Fresh VM/conformance results are recorded separately in the implementation evidence; the 999-case builtin/example inventory does not itself cover term-level List Case. Scalus language-level checks do not imply ledger certification.
Broader traversal families and the optional one-branch List Case size optimization remain deferred. The completed checkbox above records the merged bounded slice, not all possible O3 traversal optimizations.
Reviewer-approved correction preserves registry-only calls such as MathLib.floorDiv, ContextsLib.trace, and ListsLib.any when callers provide an explicit empty library list. Registry metadata is derived from actual registrations and propagated through composite/NewType lookups; no classpath scan is added to explicit-list APIs.
Regression coverage includes all six method/validator probes, expected VM results and traces, custom registries, lookup composition, isolated source-discovery fixtures, and unchanged representative pre-cleanup FLAT bytes. The original green build missed the explicit-list/registry-only combination; the corrected evidence records that gap.
Corrected local full build passed: 10,690 reported tests, zero failures/errors, 531 existing or profile-inapplicable skips. Documentation build passed (32 pages). Build & Test and Blaster Verification CI passed on the final PR head before merge; this is not a claim of formal compiler-correctness coverage.
Enabled under default PV11_SAFE and PV11_COSTED for the exact PV11 target. Producer strictness, decoding/trace/failure order and lexical scope are preserved. Aliases, escaping pairs, one-sided uses, arbitrary pair producers and map traversal are excluded. Sealed-interface switch decomposition is tracked separately in ADR-036 follow-up O4: native Pair Case for sealed-interface switch dispatch #125.
NONE/BASELINE preserve historical bytes. Safe-profile recompilation, including eligible caller-supplied PIR through compilePirToProgram, can change bytes/hashes; deployed scripts and ledger Data encoding are unchanged.
Independent Claude review supplied by the maintainer approved with notes, independently reproduced all 18 pre-change artifacts and measured costs/hashes, and reported fresh passing focused/downstream suites plus adversarial scope probes. Required documentation notes were addressed before merge.
Local full build and documentation build passed. Fresh repository test run: 10,173 passed, 531 existing/profile-inapplicable skips, zero failures. Dedicated pairCaseTest: 13 passed; CI check/build includes this task. Java and Truffle each passed all 999 PV11 conformance cases; explicit Pair Case order/strictness tests add direct lowering coverage.
Published implementation 002b2ea7 to Maven local as 0.1.0-pre17-002b2ea-SNAPSHOT; the sibling julc-examples resolved the exact plugin/compiler/AP/runtime artifacts. External results: 409 passed, 11 pre-existing skips, including all 54 transaction integration tests on the available PV11 Yaci node. Escrow Java and backend-evaluation budgets matched exactly; inspected generated artifacts contain the new pair lowering. The original report called that endpoint Ogmios/Haskell, but the ADR-032 follow-up O3: implement typed List case lowering #110 follow-up found that the same API can use Scalus. Historical evaluator identity needs configuration evidence; it is not retroactively established by the new run.
External packaging initially hit the checkout's existing missing src/main/plutus directory, reproduced with its original plugin. An isolated compileJulc fixture made packaging pass, producing the pinned 323-byte script/hash; the examples build file was preserved.
Evidence: ADR-036, Maven-local replay script. Representative isolated O4 FLAT sizes improve 360→323, 144→130 and 270→247 bytes, with non-increasing measured CPU/memory on success and failure paths.
The reported unsorted map {3:30,1:10} serializes to a203181e010a, and duplicate-key map {1:10,1:20} to a2010a0114, on pinned Scalus 1.1.0, Java and Truffle. The old Scalus 0.17/0.18 divergence no longer reproduces; no production or dependency change was needed.
Pushed regression coverage adds 80 cases covering fixed expected CBOR, literal and supplied-argument evaluation, adapter identity round-trips, nested maps, empty maps, unsorted keys and duplicate keys, plus default Scalus configuration. Fresh benchmark suite: 102 passed; Scalus module: 312 passed; zero failures or skips. No new node run was performed for this audit.
Merged 2026-09-06 at 99a69705. Map-only byte decoding preserves entry order and duplicates recursively, including FLAT Data constants; compiler lowering and encoding are unchanged.
Local fresh full build: 10,283 passed, 531 existing/profile-inapplicable skips, zero failures/errors. All 999 PV11 conformance cases passed on Java and Truffle; 48 cross-VM regression cases passed. Documentation build passed; all 41 existing external example artifacts retained exact FLAT bytes on read/re-encode.
The external artifact replay was not a fresh transaction-suite or Yaci run. Existing general CBOR nesting/size limitations remain outside this correction.
New dedicated opt-in :julc-e2e-tests:listCaseOnChainTest -Pe2e checks serialized guarded List Case shape, rule provenance, the default safe profile and the pinned live PV11 cost array. It always reruns and fails on skipped/missing profile coverage. Existing backend-only E2E setup is preserved.
Both BASELINE and PV11_SAFE passed empty, singleton, multiple-element and early-break scenarios: eight confirmed phase-2-valid spends, with exact Java/direct-Haskell budget agreement for every case. Eight wrong-total/malformed variants were rejected by Java, the backend and Haskell with the expected script failure causes; invalid variants were not submitted.
Provenance correction: Yaci Store's evaluation endpoint was using Scalus, despite an Ogmios-branded response. The test therefore invokes Haskell cardano-cli 11.0.0.0 directly against the running cardano-node 11.0.1 socket. The original endpoint remains an additional comparison, not the Haskell oracle. No devnet restart/reset was performed.
Complete-profile sizes: BASELINE 329 bytes, PV11_SAFE 285 bytes. Multi-element [2,3,4] CPU: 15,267,988 → 10,352,422; memory: 61,287 → 44,372. These include other safe rules and are not O3-only gains.
Fresh full build: 10,283 passed, 531 existing/profile-inapplicable skips, zero failures/errors; all 218 tasks executed. All 999 PV11 conformance cases passed on Java and Truffle. Focused O3/semantic/decompiler tests, existing backend-only E2E smoke tests and documentation build also passed.
Merged 2026-09-06; ADR-036 follow-up O4: native Pair Case for sealed-interface switch dispatch #125 is closed. ADR-038 extends typed Pair Case to compiler-generated sealed-interface switch decomposition. The existing integer tag dispatch, strict scrutinee evaluation, field decoding, lexical bindings, trace order and failure behavior are preserved. Public PIR referring to the historical pair binder retains the legacy expansion.
Enabled under default PV11_SAFE and PV11_COSTED for the exact PV11 target. NONE/BASELINE retain historical bytes; safe-profile recompilation can change script bytes/hashes. Deployed scripts and ledger Data encoding are unchanged.
Independent Claude review approved with no correctness findings, reproduced all 18 historical fixture rows and measured savings, and exercised nine additional adversarial PIR shapes across Java, Truffle and Scalus. Review-status documentation was reconciled before merge.
Author fresh full build: 10,292 passed, 531 existing/profile-inapplicable skips, zero failures/errors; all 218 tasks executed. Java and Truffle each passed all 999 PV11 conformance cases. Dedicated pairCaseTest passed 21 tests; documentation build passed. These full-build/conformance/docs results were author-run, while the reviewer freshly reran the affected downstream suites.
Both author and reviewer tested the external julc-examples against freshly published Maven-local snapshots: 420 cases, 409 passed, 11 pre-existing skips, including all 54 transaction integration tests. The examples build file was preserved.
The dedicated switch on-chain gate passed both BASELINE and PV11_SAFE: four confirmed valid spends and eight invalid redeemer rejections, with exact Java/backend/direct-Haskell budget agreement. Direct Haskell cardano-cli is the node oracle; the backend endpoint is Scalus. Safe scripts contain the new pair-case sites. Scalus language-level agreement does not imply ledger certification.
Representative isolated savings are 10 FLAT bytes and 491,887 CPU / 1,364 memory per executed switch pair site. Complete-profile node comparisons also include other safe rules and must not be attributed solely to this extension.
Merged into main on 2026-09-07 at 9eb685c5; Decompiler: recognize native Pair Case in sealed-interface switch dispatch #130 is closed. ADR-039 adds conservative recovery of native Pair Case and verified legacy constructor dispatch, preserving explicit pair/tag/fields bindings, ordered BigInteger tag branches and the residual fallback.
Scope resolution preserves captures through nested matches, closures and FLAT round-trips. Malformed or ambiguous shapes retain generic recovery; singleton decomposition does not invent a tag-zero check. Analysis traversal and naming/type inference support the new HIR node.
Compatibility: external exhaustive HIR visitors must handle the new DataMatch variant. Decompiled names/output can change; readable reconstruction does not promise recompilable Java or original-schema recovery. Compiler-generated script bytes/costs and ledger encoding are unchanged.
Implementation validation recorded in the PR: fresh full build executed all 218 tasks, with 10,301 passed, 531 existing/profile skips and zero failures/errors. Decompiler: 105 passed; analysis: 82 passed and 2 existing skips. Java and Truffle each passed all 999 PV11 conformance cases. Documentation build passed (32 pages).
Bounded original/reconstructed evaluation tests compare values, failure text and traces on Java and language-only Scalus before/after serialization, including malformed shapes, nested captures, unknown-tag fallback and strict unused-field failures. This is bounded regression evidence, not certification of all decompiler heuristics. No additional node run or Maven-local publication was needed for this decompiler/analysis change.
PR test(compiler): guard against builtin force-count drift #134 adds an exhaustive comparison of all 102 DefaultFun force counts against shared semantic metadata and corrects the developer guide. Deliberate extra-force (SerialiseData) and missing-force (HeadList) mutations fail the new guard. Tests/docs only.
PR test(compiler): cover serialiseData source and hash evaluation #135 adds full Java-source coverage of Builtins.serialiseData and ByteStringLib.serialiseData under BASELINE/PV11_SAFE on Java and Scalus: 16 JUnit cases, 64 VM evaluations, exact boundary/nested-constructor CBOR, and two independently computed BLAKE2b-256 commitments. Java uses the compiler's explicit PV11 target; Scalus uses language-only compatibility evaluation. Restoring the original bad force made all 16 cases fail. Tests only; no signature authorization or ledger-certification claim.
Status 2026-09-13. Filed as #137 (the yield sibling of #79) and fixed on PR #138, merged 2026-09-13 at 4cf87e70 (final head fa427a90, build + both Blaster verify checks green). Root cause: the #79 early-exit lowering only recognized ReturnStmt, so a yield inside an if took the legacy Let("_if", ifExpr, rest) path and the trailing yield ran unconditionally. The fix widens the predicate to yields owned by the enclosing switch expression (stopping at nested SwitchExpr boundaries as it already did at lambdas), rejects yield inside for-each/while bodies like return, and diagnoses a case block whose paths do not all end in a yield — including blocks ending in a trailing loop, closed after Codex review. Frontend lowering, so recompiling sources with the affected shape changes bytes/hashes under every profile including NONE/BASELINE; other sources keep their bytes (all existing goldens unchanged). No shipped stdlib/example uses the shape; the two Blaster controlled-mint verification fixtures do, and their locked artifacts, counterexample binding and manifest lock hash were refreshed (other five artifacts byte-identical, local verify.sh and the exact CI runner re-establish SMT-VALID; recorded in ADR-003 and the Blaster README). Evidence on the PR: 15 new tests (reproducer across NONE/BASELINE/PV11_SAFE on Java, Scalus, Truffle), fresh :julc-compiler:test 1,617/0, pairCaseTest 22/0, stdlib 403/0 (1 pre-existing skip), testkit 191/0, examples 81/0, docs build 32 pages. Release note added. No ADR, following the #79 precedent. Residuals: SwitchExpr boundary verified by reasoning plus unchanged fixtures rather than a dedicated test; trace order pinned via failure timing only.
The original audit record follows.
Fresh release audit, 2026-09-08, main fe4dbb9d: confirmed the pre-existing frontend finding recorded in ADR-038 evidence. No dedicated open issue for this finding was found in the audit.
Compile check with JulcCompiler.compileMethod, then evaluate with argument PlutusData.constr(0, PlutusData.integer(1)) using Java and the returned compiler target. Expected BoolConst(false); actual BoolConst(true). Both BASELINE and PV11_SAFE compiled without errors and failed the semantic assertion. The isolated audit probe ran with ./gradlew :julc-compiler:test --tests '*ReleaseNestedYieldProbeTest' -PskipSigning=true: two cases, two assertion failures, zero skips. The probe is local investigation evidence, not a merged regression test. No production files were modified.
This can turn an intended rejection into acceptance when the affected source shape is used in validation logic. It reproduces under BASELINE and is not attributable to the new Pair Case optimization. The exact control-flow repair still requires design/review.
Recommended bounded completion criteria:
Establish a focused frontend issue/design: yield exits its owning switch expression, while method returns, loop exits and inner switches retain their own scopes.
Either implement correct supported lowering or reject the unsupported nested-yield shape with an actionable diagnostic; never silently accept the wrong behavior.
Cover true/false paths, fallthrough, if/else, deeper nesting, inner switch ownership, traces and failure timing; evaluate generated programs on Java/Truffle and applicable Scalus under baseline and safe profiles.
Check nearby early-return/loop/switch regressions, deterministic output and affected artifact/hash migration. Add release notes and evidence before considering the release gate satisfied.
Finish the per-change evidence audit and final release-candidate build/documentation/external-examples/Yaci validation, then publish pre17. The latest published release remains pre16; the development snapshot is not a pre17 release.
#40 remains a non-blocking hardening candidate for shared evaluator consumers. The accepted #112–#120 slices are complete, with #113 explicitly rejected and #118 already resolved without a new rule. Residual research such as #143/#145/#146/#151, and the #163 documentation task, remains separate and open. Scalus certification remains blocked on the documented ADR-033 divergences; #55/#135 compatibility results do not remove those blockers. These priorities do not promote deferred research or Scalus certification into mandatory pre17 scope.
Profile-freeze ADR and hash-stability policy doc completed. Kept separate from implementation completion; no merged completion evidence found in this reconciliation.
Final per-change evidence audit: confirm every implemented compiler change has focused semantic, failure, deterministic-output, size/budget, Java/Truffle, and applicable Scalus evidence. Individual merged PRs report evidence, but this final cross-change audit is not established as complete.
Final release-candidate validation: affected-module tests, repository build, documentation/release notes and required external-examples/Yaci evidence for the final candidate. Individual successful PR builds do not complete this final gate; pre17 is not yet published.
Except for the explicitly selected P4/release gates above, P2 through P5 residuals are ordered follow-up candidates, not automatic release blockers. #40 remains explicitly non-blocking for this planning horizon. Moving one into the release gate requires an explicit scope decision recorded on this tracker.
Planning invariants
Do not equate successful compilation or green tests with semantic preservation.
Do not change evaluation order, strictness, failure behavior, representation, or validator boundaries without explicit tests and documentation.
Keep compiler legality separate from optimization profitability.
Preserve deterministic generated artifacts for identical inputs and configuration.
Record script-byte/hash migration for every enabled lowering.
Purpose
Track and prioritize the remaining open JuLC work after PR #109 merged ADR-032 into
mainat4401b1a8.This is a planning and sequencing umbrella. The
0.1.0-pre17label identifies the planning horizon; it does not make every deferred research item a release blocker. Correctness, deterministic evaluation, and dependency order take precedence over feature count.Status reconciliation — 2026-09-21
P4 waves 1–4 are merged: #141, #142, #144, #147, #148, #149 and #150. #112/#114/#115/#116/#117/#119/#120 are implemented within their documented bounded scopes; #113 is complete as a rejected proposal, not an implementation. PR #156 subsequently removed the compiler's pinned-cost-profile requirement without changing O9 lowering.
Correctness PRs #157, #164, #165 and #167 are also merged. PR #167 merged on 2026-09-21 at
539c3f15, automatically closing #166 as completed. The loop/switch fixes tracked here are landed. Keep this tracker open: the profile-freeze ADR/hash-stability policy, final evidence audit and release-candidate validation/publication are not established as complete. GitHub still lists pre16 as the latest release.The per-slice measurements below are historical implementation evidence, not a fresh audit of one final release artifact. Scope exclusions and residual research are not completed merely because an implementation PR merged.
Priority order
P0 — reconcile completed roadmap state
P1 — release-facing compiler correctness and backend parity
Conditional nested
yieldmiscompilation — fixed on PR fix(compiler): preserve conditional yield control flow in switch case blocks (#137) #138 (issue Conditional nested yield in switch-expression arm is silently discarded (yield sibling of #79) #137); Codex review approved and merged 2026-09-13 at4cf87e70. Freshly reproduced on mainfe4dbb9dduring the 2026-09-08 release audit and again on739ba5f1on 2026-09-13:if (condition) { yield false; } yield true;in a switch arm returnstruefor a true condition under both BASELINE and PV11_SAFE. See the reproduction and proposed completion criteria below. Treat this as release-facing correctness work; an unsupported construct must be diagnosed rather than silently miscompiled.[vm-scalus] Implement and certify protocol-aware LedgerEvaluationTarget evaluation #74 — ADR-033 adapter implementation and documented support matrix completed via PR docs(adr): define Scalus protocol-aware ledger-target evaluation #122. No Scalus ledger profile is certified: explicit targets remain fail-closed on the documented upstream/cost-coverage blockers. [vm-scalus] Implement and certify protocol-aware LedgerEvaluationTarget evaluation #74 remains open for certification; implementation completion does not imply parity.
Loop lowering: an update to a loop-body local inside an if branch is silently lost #155 / Compiler: loop reassignment of switch case-pattern variable leaves stale field reads #162 — merged PR Reject lost loop-local and switch-arm updates (#155) #157 (2026-09-20, ADR-048) rejects lost loop-local/switch-arm updates and case-pattern rebinding. This is fail-closed subset enforcement, not general mutable-local support.
Compiler: if outside a loop body silently loses nested-loop accumulator updates #161 — merged PR Preserve guarded loop accumulator state with shared joins (#161) #164 (2026-09-21, ADR-050) preserves guarded-loop accumulator updates with shared continuation joins; sequential guarded loops no longer duplicate the tail exponentially.
Outer-loop/switch-arm validation follow-up — merged PR Fix switch-arm validation inside outer loops (stacked on #164) #165 (2026-09-21): check the selector under outer-loop restrictions; keep arm ownership and actual nested-loop checks. Arm-local guarded loops can yield results inside an outer loop.
Switch case-pattern fields omit Bool and String Data decoding #166 — Bool/String switch-pattern field decoding: merged PR Fix Bool and String decoding in switch-pattern fields (#166) #167 (2026-09-21,
539c3f15, ADR-051); issue automatically closed. Switch-pattern fields now reuse the shared decoder; the duplicate incomplete table is removed. Compiler: loop reassignment of switch case-pattern variable leaves stale field reads #162 rebinding rejection and Fix switch-arm validation inside outer loops (stacked on #164) #165 ownership checks remain intact. Source/direct-PIR regressions cover all four optimization levels on Java/Truffle/Scalus; Fix switch-arm validation inside outer loops (stacked on #164) #165's exact integer-field example is also pinned. Affected Bool/String script bytes/hashes/budgets change on recompilation; this is separate from strict nested-field boundary validation. Decompiler recognition coverage for these field shapes remains a nonblocking follow-up, not an unmerged part of this fix.#40 is not a prerequisite for #74 or a pre17 release blocker. New evaluation work should still avoid introducing additional shared mutable configuration.
P2 — non-blocking hardening, cleanup, and fidelity work
JulcTransactionEvaluator. Current Yano main serializes the REST evaluation path and creates a fresh JuLC evaluator per invocation, so its endpoint does not expose the race. Keep the public issue open for broader API hardening.StdlibLookup.registeredClassNames()under ADR-035; the compiler hardcode is removed entirely, including Builtins. Both scanning and explicit-library-list entry points remain supported.serialiseData(map)failures no longer reproduce with pinned Scalus 1.1.0. Java, Truffle and Scalus agree on the expected bytes. This does not certify Scalus ledger budgets.c2142e92(test-only).8ed2a885cherry-picked onto main, moved toorg.julclang.benchmark.conformance, redundant literal-serialisation path dropped (already pinned by PR Preserve duplicate map entries in Plutus Data CBOR and FLAT decoding #127'sMapDecoderProgramFidelityTest), stale decoder-limitation comment corrected; keeps the 56 cases with no other coverage (supplied-argument serialisation, default Scalus configuration, adapter identity round-trips). Fresh:julc-benchmark:test126/0/0.99a69705), governed by ADR-037. CBOR/FLAT read-back preserves ordered duplicate associations; malformed map checks and cross-VM regressions are included. General CBOR resource hardening remains outside this fix.serialiseDatafixed by merged PR fix(compiler): emit serialiseData without a force wrapper #133 (2026-09-07). Follow-up PRs test(compiler): guard against builtin force-count drift #134 and test(compiler): cover serialiseData source and hash evaluation #135 merged 2026-09-08: exhaustive force-count drift guard and Java-source serialization/hash regression matrix.MathLib.expModalready lowers directly toExpModInteger; ordinarypow(...) % modulusremains untouched because its semantics differ.P3 — next compiler optimization slices
8af52387). ADR-036 covers proven once-bound UnConstrData pairs in strict constructor boundaries; sealed-interface switch dispatch was subsequently completed in ADR-036 follow-up O4: native Pair Case for sealed-interface switch dispatch #125 / PR Extend PV11 Pair Case lowering to sealed-interface switches (#125) #129.switchdispatch completed and closed via merged PR Extend PV11 Pair Case lowering to sealed-interface switches (#125) #129 (2026-09-06), governed by ADR-038. Independent review approved the bounded O4 extension.9eb685c5), governed by ADR-039. Native and verified legacy dispatch recovery preserve explicit bindings and fallback; compiler-generated UPLC is unchanged.Each slice must preserve evaluation order, decode/failure timing, representation boundaries, deterministic bytes, source attribution, and cross-backend behavior. Do not replace these with untyped UPLC peepholes.
P4 — PV11 lowering completion (scope decision 2026-09-13: do before the remaining release gates)
Scope decision (maintainer, 2026-09-13). All P4 items move ahead of the evidence-audit and release-candidate gates. Rationale: every one of them lowers to PV11-only builtins under
PV11_SAFE/PV11_COSTEDand changes emitted bytes/hashes for eligible programs; pre17 is a preview where that churn is acceptable, and landing it now lets the next milestone freeze the default PV11 rule set so later releases change PV11 hashes only for announced correctness fixes.NONE/BASELINEretain historical bytes for these optimization slices; that is not a blanket guarantee for subsequent correctness fixes such as #161/#166, whose affected-script changes apply at all levels. Each slice follows the #110/#111 pattern: ADR → bounded implementation → semantic/failure/deterministic-hash/size-budget/Java+Truffle+Scalus evidence → independent review → node evidence where material → release-note migration line → Blaster fixture check (verification/blaster/fixtures).Original execution order (2026-09-13; completion status below):
ChooseUnit, so the real candidate is[(λ_. rest) e]→case e [rest], which needs a typed Unit node — expected outcome: reject). Implement whichever is accepted.PV11_SAFErule set frozen; new rules only in a new opt-in profile) and the hash-stability policy doc, followed by the evidence audit, release-candidate validation, and the pre17 publish.Items:
PV11_SAFE(pv11.o5.case-integer); failure contract pinned; VM integer-Case range hardening; decompiler recognizer; goldens fromc2142e92; on-chain Haskell-exact; external examples 420/0.ChooseUnit; census of 58 shipped validators shows no typed-unit statement population worth a new PIR surface. Decision task complete; this is not an implemented optimization.unValueDataconversion of one variable atPV11_SAFE(pv11.o8.value-sharing): shared only when the conversion is already the first non-trivial evaluation of its scope, so results, traces and failure text are unchanged; per-iteration loop conversions hoist (−1.5M CPU on a 3-iteration fixture); loss bounded at 48,000 CPU per binding on paths that never reach a second occurrence;ValueDatasinking/cancellation and Data-sideValuesLibsharing recorded as out of scope; no shipped example changes hash; goldens from5e32bbcd.pv11.o9.list-to-array, fires only atPV11_COSTED): aJulcListvariable indexed at two or moregetsites, or at a site inside a loop/recursive helper/inlined callback, is converted once withListToArrayand its sites becomeIndexArray, bound at the innermost evaluated-once sub-term (two-site output byte-identical to a hand-writtentoArray()). Failure contract: out-of-range index fails atIndexArray(same point, smaller budget, different off-chain text), observable from validators through the strict boundary, pinned on Java/Truffle/Scalus. Break-even measured from the pinned profile and reproduced exactly (two sites at 0/1 gain up to 44 elements; WingRiders-shaped loop with 16 requests 349.6M → 81.9M CPU). Only bindings proven to hold a list are promoted (proven-name environment; casts and every alias of them, including the loop lowering'slet xs = xs, are never promoted; callback parameters never trusted); the residual through helper parameters/returns and loop state is pinned as a documented costed-only divergence (CAST_HELPER,CAST_LOOP_STATE) — retained as the merged costed-only contract, not a claim that unchecked casts preserve safe-profile behavior.DropListgetlowering measured and filed as Decide: lower JulcList.get(i) to a guarded DropList form instead of the recursive traversal (measured under ADR-043 / #115) #143. Goldens from940dc65b(20 fixtures × 4 levels); default level byte-identical everywhere; external examples atpv11-costedbyte-identical (no promotable shape in the compiled corpus; the real WingRiders pool validator compiled with the CLI drops 1,943 → 1,699 bytes).JulcArray.of(a, b, ...)is the typed array literal: lowered toListToArrayover theJulcList.oflist literal, with the Data-encoded element representationJulcArray<T>already has (a native Value element isJULC0041); PV11 only.ArraySemanticsinjulc-corepins the three array builtins (bothIndexArrayfailure texts) and the VM delegates to it.ArrayLiteralFoldPass(rulepv11.o10.array-literal-fold,PV11_SAFE, switchable; the ADR-045 machinery is now the abstractLiteralFoldPass) foldsListToArrayof a list literal to an array constant,LengthOfArrayand literal-indexIndexArrayover it to their results, and thegetdecode only over elements it produced (never a pre-existing Data constant); out-of-range literal indexes stay and fail with the builtin's text;MultiIndexArrayis never emitted. Additive: no existing program contains an array constant or converts a list literal to an array; every golden suite, the Blaster lock and all 41 example validators are byte-identical at every level. 19 fixtures × 4 levels × rule off/on on three VMs; e.g.JulcArray.of(1, 2, 3).get(1)42 → 6 bytes, 1,238,594 → 16,100 CPU; a runtime-indexed table 49 → 34 bytes, 1,435,338 → 625,598 CPU on every path. Two review agents, no blocking finding (fixes: objective on the Bool fold, requirement key under the qualified class name, stronger failure/budget assertions,vardivergence pinned). Open: a nativearray integerrepresentation behind its own marker type, sharing repeated length/access on one array, map elements and negated literals not read as literals, on-chain gate for an embedded array constant.JulcG1/JulcG2/JulcMlResultand the native listsJulcScalars/JulcG1Points/JulcG2Points(opaque PIR types under the O7 isolation rules,JULC0041/JULC0042); everyBuiltins.bls12_381_*andBlsLibmethod retyped (compressthe only way to bytes,uncompressthe only way back); the registry's per-builtin signature table rejects a wrong group, a byte string, Data or a Data list at compile time. Explicitg1MultiScalarMul/g2MultiScalarMulover native lists built only by the intrinsicsscalars/g1Points/g2Pointsand the convertersscalarsFromList/g1PointsFromCompressed/g2PointsFromCompressed(no Data wrapper possible); MSM semantics pinned on three VMs (all scalars validated first at 512 bytes, zip to the shorter list, empty sum is the identity). No pass, no fusion. Additive: no corpus/golden/Blaster program uses the BLS surface; avar-style BLS program compiles to identical bytes under both APIs; all 41 example validators byte-identical at every level. Crossover on the pinned profile: MSM smaller from three points, cheaper in CPU from seven (net of the shared point hashing: about 322M to enter + 25M per point versus 77M per point). Review fixes: element typing ofscalarsand the converters; same-class helper arguments andreturnexpressions now under the native check. Migration:byte[]locals of BLS values are nowJULC0041(varunaffected). Open: literal-scalar bound check, chain fusion (costed), inverse converter, on-chain gate.Builtinsintrinsics (emptyValue()= a UPLC Value constant;singletonValue(policyId, tokenName, quantity)andlovelaceValue(quantity)=insertCoininto it, with the builtin's zero/negative/key/range rules), gated by the PV11 Value-constant capability.NativeValueSemanticsinjulc-corepins the seven Value builtins (exact failure texts) and the VM now delegates to it, so the compiler fold and the runtime share one implementation.ValueLiteralFoldPass(rulepv11.o14.value-literal-fold,PV11_SAFE, switchable) folds a saturated all-literal call of any Value builtin (bare, through aNativeValueLibwrapper, or over once-bound literal locals) into its result when the semantics succeed and the literal's FLAT encoding is not larger than the call's; rejected calls stay as written (same failure text on Java/Truffle/Scalus); no algebraic identities. Additive: no existing program contains a Value literal, so every golden suite, the Blaster lock and all 41 example validators are byte-identical at every level (the producers are intrinsics precisely so that no dead library binding is added). 29 fixtures × 4 levels × rule off/on on three VMs; e.g. a literal lookup 40 → 7 bytes, 883,863 → 16,100 CPU. Two review agents found one blocking hole (a wrapper with an unused parameter dropped that argument unexamined), fixed with probes; the objective is measured in FLAT bits against the call site. Open: CPU-aware objective forfromDataof large Data literals,toData(emptyValue())stays a call by two bytes, negated literals (new BigInteger("-5")), folding the empty-Value idiom (hash-moving),NativeValueLibspelling once bindings are pruned, on-chain gate for an embedded Value constant.D(headList(tailList^k(sndPair(unConstrData(x))))), onewrapDecodearm, matched outermost-first, a matched unit a leaf for collector and rewriter alike) and the fields prefix (sndPair(unConstrData(x))) join the native Value conversion; rounds run chains to a fixed point (chain on chain), then the prefix, then Value; units inside transitively dead bindings (uncalled library methods) are skipped; the body of a single recursive binding of a lambda can lead (the per-sitegetlowering). Rulepv11.o15.projection-sharingatPV11_SAFE, failure-text neutral on Java/Truffle/Scalus; exact cost model pinned (after = before + (2+k)·16,000 − (k−1)·unit; loss bound 48,000 CPU per binding). Each PIR rule is now individually switchable (CompilerOptions.disableOptimizationRule,JULC0043for unknown ids), as ADR-032 follow-up O15: add minimal representation-aware let sharing #120 requires. Hashes move at the default level: 26 of 41 example validators change hash or size (the rule's trigger shape is in most validators); the hash-stability policy doc gets its precedent here. Goldens from1fd98c93(24 fixtures × 4 levels, self-verifying with the rule off); O8 fixtures byte-identical with every rule on; costed-only handoff: O9 now promotesx.items().get(i)sites because the list is bound first. Two review agents + advisor, all should-fixes folded. Open: units that do not lead,ValuesLibrepeats, provenance from the final artifact,compareToreceiver duplication (compareTo lowering evaluates a non-trivial receiver and argument twice #146), loop self-alias, plugin/CLI switch exposure.Post-implementation configuration correction:
pv11-costedcompiles without a pinned compiler cost profile. O9 eligibility is structural, not a runtime numeric profitability gate; pinned cost models remain benchmark/evaluation inputs. The neutralplutus-v3-pv11-costs-v1ID preserves the original parameters and hash, and the old ID remains a compatibility alias. Compiler provenance no longer attributes an unused cost profile. This does not constitute the still-pending profile freeze/hash-stability policy.Dependency/scope notes:
P5 — verification research and roadmap trackers
ContextsLib.signedBy; substantial ADR-first research work and not a pre17 release gate unless verification becomes the explicit release objective.Merged compiler slice: #110 / PR #123
ListMatchlowers eligible for-each traversals to guarded PV11 List Case. The originalNullListguard preserves malformed-runtime-value failures and element decoding remains strict.PV11_SAFEand inPV11_COSTEDfor the explicit PV11 target.NONE/BASELINEretain historical bytes. Recompilation with the updated safe profile can change script bytes/hashes; deployed scripts and ledger Data encodings are unchanged.Merged cleanup and decision: #22 / #118 / PR #124
MathLib.floorDiv,ContextsLib.trace, andListsLib.anywhen callers provide an explicit empty library list. Registry metadata is derived from actual registrations and propagated through composite/NewType lookups; no classpath scan is added to explicit-list APIs.stdlibClassFqcns()inventory remains a non-blocking follow-up because it includes source-only classes. Make JulcTransactionEvaluator thread-safe (currently requires one instance per thread) #40 remains non-blocking; [vm-scalus] Scalus backend serialiseData(map) sorts + dedups, diverging from the node (preserve order + dups) #55 is now closed after the pinned-version audit; the distinct [vm-scalus] Scalus backend serialiseData(map) sorts + dedups, diverging from the node (preserve order + dups) #55 regression coverage merged in PR test(benchmark): merge reconciled #55 map serialization fidelity coverage #140; core decoder fidelity is complete via PR Preserve duplicate map entries in Plutus Data CBOR and FLAT decoding #127. ADR-032 follow-up O4: implement typed Pair destructuring analysis #111 is complete via PR Implement typed PV11 Pair Case lowering for strict constructor boundaries #126; ADR-036 follow-up O4: native Pair Case for sealed-interface switch dispatch #125 is now complete via PR Extend PV11 Pair Case lowering to sealed-interface switches (#125) #129. The decompiler follow-up Decompiler: recognize native Pair Case in sealed-interface switch dispatch #130 is completed by merged PR Recover native Pair Case constructor dispatch in the decompiler (#130) #131.gradle.propertiesnow identifies development as0.1.0-pre17-SNAPSHOT; this is not a published pre17 release.Merged compiler slice: #111 / PR #126
8af52387; ADR-032 follow-up O4: implement typed Pair destructuring analysis #111 is closed. Typed PIRPairMatchreplaces direct FstPair/SndPair uses of the same once-bound UnConstrData result only when the local-use and native representation proof succeeds.002b2ea7to Maven local as0.1.0-pre17-002b2ea-SNAPSHOT; the sibling julc-examples resolved the exact plugin/compiler/AP/runtime artifacts. External results: 409 passed, 11 pre-existing skips, including all 54 transaction integration tests on the available PV11 Yaci node. Escrow Java and backend-evaluation budgets matched exactly; inspected generated artifacts contain the new pair lowering. The original report called that endpoint Ogmios/Haskell, but the ADR-032 follow-up O3: implement typed List case lowering #110 follow-up found that the same API can use Scalus. Historical evaluator identity needs configuration evidence; it is not retroactively established by the new run.Completed fidelity audit: #55
{3:30,1:10}serializes toa203181e010a, and duplicate-key map{1:10,1:20}toa2010a0114, on pinned Scalus 1.1.0, Java and Truffle. The old Scalus 0.17/0.18 divergence no longer reproduces; no production or dependency change was needed.Merged decoder fidelity: PR #127
99a69705. Map-only byte decoding preserves entry order and duplicates recursively, including FLAT Data constants; compiler lowering and encoding are unchanged.Completed #110 node gate / merged PR #128
test/110-list-case-onchain-evidence, commite8f84ec3(PR Complete List Case on-chain validation with direct Haskell budgets #128), based on merged main99a69705. No compiler, VM or encoder changes were needed; the bounded lowering remains the implementation from PR Implement guarded PV11 List-case lowering for for-each loops #123.:julc-e2e-tests:listCaseOnChainTest -Pe2echecks serialized guarded List Case shape, rule provenance, the default safe profile and the pinned live PV11 cost array. It always reruns and fails on skipped/missing profile coverage. Existing backend-only E2E setup is preserved.[2,3,4]CPU: 15,267,988 → 10,352,422; memory: 61,287 → 44,372. These include other safe rules and are not O3-only gains.adr/evidence/034-list-case-onchain.mdand its transaction CSV in merged PR Complete List Case on-chain validation with direct Haskell budgets #128. PR Complete List Case on-chain validation with direct Haskell budgets #128 merged on 2026-09-06 atf27f1ca0; ADR-032 follow-up O3: implement typed List case lowering #110 is closed. The bounded lowering and its node evidence are complete. Broader traversal families remain deferred.Merged compiler slice: #125 / PR #129
Merged decompiler follow-up: #130 / PR #131
mainon 2026-09-07 at9eb685c5; Decompiler: recognize native Pair Case in sealed-interface switch dispatch #130 is closed. ADR-039 adds conservative recovery of native Pair Case and verified legacy constructor dispatch, preserving explicit pair/tag/fields bindings, ordered BigInteger tag branches and the residual fallback.DataMatchvariant. Decompiled names/output can change; readable reconstruction does not promise recompilable Java or original-schema recovery. Compiler-generated script bytes/costs and ledger encoding are unchanged.Merged serialization correctness and prevention: PRs #133–#135
SerialiseData. Its direct PIR regression checks four exact CBOR vectors on Java PV10/PV11. Recompiling affected programs changes script bytes/hashes; already deployed scripts and the serialization format are unchanged.DefaultFunforce counts against shared semantic metadata and corrects the developer guide. Deliberate extra-force (SerialiseData) and missing-force (HeadList) mutations fail the new guard. Tests/docs only.Builtins.serialiseDataandByteStringLib.serialiseDataunder BASELINE/PV11_SAFE on Java and Scalus: 16 JUnit cases, 64 VM evaluations, exact boundary/nested-constructor CBOR, and two independently computed BLAKE2b-256 commitments. Java uses the compiler's explicit PV11 target; Scalus uses language-only compatibility evaluation. Restoring the original bad force made all 16 cases fail. Tests only; no signature authorization or ledger-certification claim.fe4dbb9d(merge test(compiler): cover serialiseData source and hash evaluation #135). This is recorded PR evidence, not a fresh full-release validation of the final release candidate.Conditional nested yield correctness — #137 / PR #138 (merged
4cf87e70)Status 2026-09-13. Filed as #137 (the
yieldsibling of #79) and fixed on PR #138, merged 2026-09-13 at4cf87e70(final headfa427a90, build + both Blaster verify checks green). Root cause: the #79 early-exit lowering only recognizedReturnStmt, so ayieldinside aniftook the legacyLet("_if", ifExpr, rest)path and the trailingyieldran unconditionally. The fix widens the predicate to yields owned by the enclosing switch expression (stopping at nestedSwitchExprboundaries as it already did at lambdas), rejectsyieldinside for-each/while bodies likereturn, and diagnoses a case block whose paths do not all end in ayield— including blocks ending in a trailing loop, closed after Codex review. Frontend lowering, so recompiling sources with the affected shape changes bytes/hashes under every profile including NONE/BASELINE; other sources keep their bytes (all existing goldens unchanged). No shipped stdlib/example uses the shape; the two Blaster controlled-mint verification fixtures do, and their locked artifacts, counterexample binding and manifest lock hash were refreshed (other five artifacts byte-identical, localverify.shand the exact CI runner re-establish SMT-VALID; recorded in ADR-003 and the Blaster README). Evidence on the PR: 15 new tests (reproducer across NONE/BASELINE/PV11_SAFE on Java, Scalus, Truffle), fresh:julc-compiler:test1,617/0,pairCaseTest22/0, stdlib 403/0 (1 pre-existing skip), testkit 191/0, examples 81/0, docs build 32 pages. Release note added. No ADR, following the #79 precedent. Residuals:SwitchExprboundary verified by reasoning plus unchanged fixtures rather than a dedicated test; trace order pinned via failure timing only.The original audit record follows.
Fresh release audit, 2026-09-08, main
fe4dbb9d: confirmed the pre-existing frontend finding recorded in ADR-038 evidence. No dedicated open issue for this finding was found in the audit.Minimal source shape:
Compile
checkwithJulcCompiler.compileMethod, then evaluate with argumentPlutusData.constr(0, PlutusData.integer(1))using Java and the returned compiler target. ExpectedBoolConst(false); actualBoolConst(true). Both BASELINE and PV11_SAFE compiled without errors and failed the semantic assertion. The isolated audit probe ran with./gradlew :julc-compiler:test --tests '*ReleaseNestedYieldProbeTest' -PskipSigning=true: two cases, two assertion failures, zero skips. The probe is local investigation evidence, not a merged regression test. No production files were modified.This can turn an intended rejection into acceptance when the affected source shape is used in validation logic. It reproduces under BASELINE and is not attributable to the new Pair Case optimization. The exact control-flow repair still requires design/review.
Recommended bounded completion criteria:
yieldexits its owning switch expression, while method returns, loop exits and inner switches retain their own scopes.Remaining follow-ups — reconciled 2026-09-21
Resolve the confirmed nested-yield correctness finding above.Done on PR fix(compiler): preserve conditional yield control flow in switch case blocks (#137) #138 (Conditional nested yield in switch-expression arm is silently discarded (yield sibling of #79) #137), merged4cf87e70. That was the 2026-09-13 audit state; later Loop lowering: an update to a loop-body local inside an if branch is silently lost #155/Compiler: loop reassignment of switch case-pattern variable leaves stale field reads #162/Compiler: if outside a loop body silently loses nested-loop accumulator updates #161 fixes and Switch case-pattern fields omit Bool and String Data decoding #166 (PR Fix Bool and String decoding in switch-pattern fields (#166) #167) are now merged.Close the release-note and evidence gap— done, PR docs: serialiseData force-fix release note and re-enabled stdlib test (#121 follow-up) #139 merged 2026-09-13 atcf9dca0a. Adds the fix(compiler): emit serialiseData without a force wrapper #133serialiseDataforce-fix release note with its affected-script hash migration and re-enables the stale@DisabledByteStringLibTestserialiseData case, strengthened to the exact CBOR (0x18 0x2a); fresh:julc-stdlib:test403/0/0 with zero skips, docs build 32 pages.Reconcile and merge the distinct remaining [vm-scalus] Scalus backend serialiseData(map) sorts + dedups, diverging from the node (preserve order + dups) #55 coverage from— done, PR test(benchmark): merge reconciled #55 map serialization fidelity coverage #140 merged8ed2a885c2142e92; see the P2 entry above for what was kept and dropped. No PR exists for that branch. PR Preserve duplicate map entries in Plutus Data CBOR and FLAT decoding #127 already covers serialized map literal fidelity and equality with supplied arguments; the old branch adds direct argument serialization, adapter identity and default-Scalus paths. Preserve useful missing coverage and update its stale decoder-limitation comment instead of assuming all 80 cases are new.Complete the P4 PV11 lowering waves.Done via PRs feat(compiler): PV11 integer Case dispatch for sealed switches (ADR-041, #112) and O6 decision (#113) #141/feat(compiler): share repeated native Value conversions at the safe profile (ADR-042, O8) #142/feat(compiler): promote repeatedly indexed lists to PV11 arrays at the costed profile (ADR-043, O9, #115) #144/feat(compiler): share repeated record field projections at the safe profile (ADR-044, O15) #147/feat(compiler): typed native Value literals and literal folding at the safe profile (ADR-045, O14) #148/feat(compiler): typed array literals and literal folding at the safe profile (ADR-046, O10) #149/feat: typed BLS12-381 values, native scalar/point lists and explicit multi-scalar multiplication (ADR-047, O11, #117) #150. The profile-freeze ADR and hash-stability policy doc remain pending; Decouple compiler optimization from pinned cost profiles #156's cost-profile cleanup does not replace them.#40 remains a non-blocking hardening candidate for shared evaluator consumers. The accepted #112–#120 slices are complete, with #113 explicitly rejected and #118 already resolved without a new rule. Residual research such as #143/#145/#146/#151, and the #163 documentation task, remains separate and open. Scalus certification remains blocked on the documented ADR-033 divergences; #55/#135 compatibility results do not remove those blockers. These priorities do not promote deferred research or Scalus certification into mandatory pre17 scope.
Proposed pre17 release gate
Required before calling this plan complete:
4cf87e70after Codex review; Blaster controlled-mint artifacts refreshed in the same PR. Added as a recommended release-facing correctness gate in the 2026-09-08 audit; this is not a new optimization feature.Except for the explicitly selected P4/release gates above, P2 through P5 residuals are ordered follow-up candidates, not automatic release blockers. #40 remains explicitly non-blocking for this planning horizon. Moving one into the release gate requires an explicit scope decision recorded on this tracker.
Planning invariants