Skip to content

[M11 correctness] Close supported-surface Workshop emission and round-trip divergences #87

Description

@Teakowa

Parent: #82
Depends on: #86

Goal

Fix the class-3 emission divergence: string values inside Array(...) initializers must be emitted in the pinned oracle's Custom String("...") form, so supported-surface programs with string arrays produce N-level-equal Workshop output.

Context (QA class-3 finding, first of M11)

globalvar x = ["a","b"] emits native Array("a","b") vs oracle Array(Custom String("a"), Custom String("b")). Corpus instance: pixelart.opy:35 globalvar owo = [' ', ...] diverges at normalized char 411; pixelart full compile exit 0 and settings-section equality hold, but full-program normalized equality fails. Arrays + strings are inside the declared supported surface (support-matrix); no documented intent (not class 5); complete evidence (not class 6). Pre-existing M8 emitter bug, surfaced by the first full-program compile; not a settings regression. The 6 v1-gates fixtures pass because none contains a string array.

Scope (bounded emitter correctness fix)

  • crates/wright-workshop/src/emitter.rs: render string values in the contexts the pinned oracle wraps with Custom String("...") — known instance: direct string elements of Value::Array (the shared native/adapter emission path covers both). Match the oracle's string-wrapping in value positions for every context evidenced by the minimal repro and the corpus snapshots; no context that currently matches the oracle may change.
  • Regression tests: minimal repro (globalvar x = ["a","b"] + one rule) byte-asserted Array(Custom String("a"), Custom String("b")); pixelart full-program normalized-equality check (v1 normalizer: debug-HUD collapse + whitespace collapse); the 6 v1-gates rows still green; the 7 settings sections still equal (pixelart 256, santa 350, broken-weapons 511, client-to-server 297, parabola 136, crosshair 144, inputhud 476 — whitespace-collapsed); ws-parser roundtrip for the new spelling.

Non-goals (hard)

  • No deferred syntax (settings semantics, \, ++/--, dicts, """, #!obfuscate, expression-level in/not in, named arguments, range literals, Team.2, inline conditionals, comprehensions, bytes literals, multiline strings).
  • No normalizer change to mask the divergence (would manufacture a class 5 without documented intent).
  • No v1-gates FIXTURES change. A pixelart gate row may be proposed later only after verification, with inventory evidence.

Acceptance criteria (QA-verifiable)

  • AC-1: minimal repro emits Array(Custom String("a"), Custom String("b")) (byte-asserted test); pinned-oracle artifact identical under the v1 normalizer.
  • AC-2: pixelart full-program normalized equality (normalizedEqual: True in target/m11-nlevel.json, nativeExit 0) — or, if any divergence remains, QA records the precise remaining divergence list and reclassifies each.
  • AC-3: previously-passing emission contexts unchanged: v1-gates 6/6 (FIXTURES unchanged); the 7 settings sections still equal; roundtrip/emitter suites green.
  • AC-4: no new class-3 findings: per-program first-failure matrix unchanged or strictly improved (pixelart becomes the first full-compile + full-equality program); oracle 20/20; adapter 22/22; differential green (PARITY_CASES 7 rows; a pixelart row only if native==adapter HIR subtree equality is separately verified — never forced); all cargo suites; six CI jobs green.
  • AC-5: inventory refreshed at the fix commit (class-3 resolved; parity baseline; pixelart row status exactly as verified).

Admin note

#86 closes with AC-3 recorded as contingent-not-justified (its acceptance record is complete); this issue carries the pixelart full-program row forward (N-level AC; PARITY_CASES row contingent on a new native==adapter HIR assertion).

PM amendment — extended acceptance criteria (2026-08-14)

QA verification at dbd342e (docs/opy/m11-issue87-verification.md) found AC-2 not met: pixelart full-program normalized equality fails (279 regions / 10,481 positions, all in the 9 artwork string-array sites) with three recorded mechanisms. The residual set is the same emission contract as this issue; the acceptance criteria are extended within #87 (no new issue).

  • AC-1 (stand): minimal repro Array(Custom String("a"), Custom String("b")) byte-equal; the test artifact uses a real action body (no pass), oracle-identical end to end.
  • AC-2 (amended): pixelart full-program normalized equality (normalizedEqual: True in target/m11-nlevel.json) OR a residual list shrinking to exactly what remains after A/B/C, each item reclassified; mechanisms A and B must be gone.
  • AC-3 (new): long-string split parity — oracle rule: non-final segments = exactly 125 content chars + {0} (128 total), chained Custom String("...{0}", Custom String("...")), final segment = remainder; synthetic 300-char (3 segments) and 1000-char (8 segments) repros byte-equal to the pinned oracle artifact (byte-quoted in tests); char-count basis pinned from the oracle artifact.
  • AC-4 (new): value-string re-escape parity — globalvar x = "a\nb" emits Custom String("a\nb") with literal \n (0x5C 0x6E), byte-equal to oracle; \ " \t \r handled per oracle evidence; the 7 settings sections still equal (256/350/511/297/136/144/476) proving the settings path untouched.
  • AC-5 (new): empty-rule drop parity — pass-only rule and condition-without-actions repros emit no rule; artifact byte-equal to the oracle.
  • AC-6 (new): roundtrip fixed-point for the new spellings — split strings and re-escaped strings parse and re-emit byte-identically; a bounded ws-parser gain for the oracle's Custom String chain shape is authorized if the generic call path cannot consume it (self-contained; no general call rework).
  • AC-7 (stand): previously-passing contexts unchanged — v1-gates 6/6 (FIXTURES unchanged), emitter/driver/roundtrip suites green.
  • AC-8 (stand): 12-program first-failure matrix unchanged; no new class-3; oracle 20/20; adapter 22/22; differential green (PARITY_CASES 7; a pixelart row only with a committed native==adapter HIR subtree assertion — never forced); all cargo suites; six CI jobs green.
  • AC-9 (stand): pixelart parity-row decision per AC-8; N-level row status exactly as verified.
  • AC-10 (new): inventory refresh at the fix commit (class-3 A/B/C resolved; pixelart row status; supersede the 4f41cdf residual sections where they differ).

Non-negotiables: no normalizer masking; no v1-gates FIXTURES change; no deferred syntax; the settings emission path untouched.

PM amendment 2 — final correctness batch (AC-11..AC-14, 2026-08-14)

QA's pre-gate scan (docs/opy/m11-inventory-final.md, a87b0ec) resolved the pixelart class-3 family and surfaced three further class-3 findings on supported surface, all repro-pinned: (1) trailing-if End; omission + ws-parser roundtrip asymmetry; (2) .format() constant folding (semantically equivalent); (3) non-default numeric globalvar initializers dropped (semantic — variables start at 0; live adapter carries them; the support-matrix claim is refuted and corrected in-batch). All three are authorized as one bounded batch inside #87.

  • AC-11 (numeric initializers): j = 5 preserved in HIR and emitted Set Global Variable(j, 5) in the Initialize rule byte-equal to oracle; h = 0 dropped (matching oracle and adapter); k = 0.0 preserved with the oracle's spelling; bare-index form untouched; new synthetic fixture (e.g. synthetic/declarations-numbers) with adapter fixture + oracle.json + PARITY_CASES row (HIR parity) + N-level; support-matrix claim corrected: "integer-0 literal initializers are dropped from HIR (matching the reference adapter); non-zero and non-integer numeric initializers are preserved".
  • AC-12 (trailing-if): rule-final if/if-else emitted without the closing End; byte-equal to oracle (repro pins a-d: rule-final if, rule-final if-else, if inside rule-final for/while, middle-of-rule if which keeps End); ws parser accepts the oracle's spelling (bounded, rule-block-close-only); roundtrip fixed-point green.
  • AC-13 (format folding): all-constant numeric-arg .format() folds to Custom String("value: 3") byte-equal (repros: single arg, multi-arg, float spelling 0.5); variable-arg case unchanged byte-equal; HUD/debug path untouched; folded output feeds splitting/re-escaping.
  • AC-14 (no regression): v1-gates 6/6 (FIXTURES unchanged); 7 settings sections equal; pixelart N-level still True; 12-program first-failure matrix unchanged; differential green (PARITY_CASES 7 or 8 per AC-11); oracle 20/20; adapter 22/22; all cargo suites; clippy 0; fmt clean; six CI jobs green at the batch commit; inventory refreshed (superseding a87b0ec's findings sections); no new class-3 anywhere.

Non-negotiables: no deferred syntax; no normalizer masking; no v1-gates FIXTURES change; no settings-surface change; no E-level work. After the checklist passes, the #82 final gate runs (accept-with-limitations criteria per the #82 decision record).

PM amendment 3 — final batch (AC-15..AC-17, 2026-08-14)

QA's final checklist (ea2c1a4) verified the full batch but the final scan found two more class-3 items, both emission/roundtrip canonicalization on matrix-listed supported surface (corpus-unexercised, zero first-failure/parity impact): (1) format placeholder canonicalization — "v: {}".format(x) native emits {}, oracle emits {0}; (2) playervar member reads in value positions — native Event Player.p, oracle (Event Player).p, and both spellings fail the native ws parser (native emission does not round-trip). Authorized as the final batch:

  • AC-15 (placeholder canonicalization): implicit {} rewrites to the oracle's explicit numbered form per pinned repros (single/multi/mixed explicit+implicit/repeated-arg shapes — pin from oracle artifacts FIRST); HIR format text stays {} in both producers (differential unaffected); constant-fold and HUD/debug paths untouched; un-pinned shapes unchanged with a fixed-point parseability assertion.
  • AC-16 (playervar reads): g = eventPlayer.p emits Set Global Variable(g, (Event Player).p) byte-equal to oracle; ws parser accepts the oracle's spelling (bounded); roundtrip fixed-point green; the SET form and method-call receivers untouched.
  • AC-17 (closure scan + no new class-3): the ENUMERATED matrix-surface closure scan runs clean — every emitter-produced spelling self-roundtrips (parse(emit(x)) byte-identical) and matches the oracle's canonical form where a repro compiles; 12-program first-failure matrix unchanged; v1-gates 6/6 (FIXTURES unchanged); 7 settings sections equal; pixelart normalizedEqual True (19,925/19,925); PARITY_CASES 8 unchanged; oracle 21/21; adapter 23/23; all cargo suites; clippy 0; fmt clean; six CI jobs green at the batch commit; inventory refreshed (superseding ea2c1a4's open-items sections).

Scan boundary (decision): the no-open-class-3 criterion is scoped to the enumerated matrix-listed emission surface (self-roundtrip closure + oracle-canonical repros); constructs outside the matrix are deferred by definition; this is the LAST scan — no further ad-hoc scans. After (a)+(b) and a clean closure scan, the #82 final gate runs as accept-with-limitations per the #82 decision record.

PM amendment 4 — final single item (AC-18, 2026-08-14)

The closure scan's sole remaining finding (augmented playervar assignment eventPlayer.p += 1: native Set Player Variable(..., Add((Event Player).p, 1)) vs oracle Modify Player Variable(Event Player, p, Add, 1); matrix-listed, oracle success, semantically equivalent, both spellings roundtrip, zero corpus coverage) is accepted as class 3 and authorized as the final single-item batch:

  • AC-18: pin the oracle's per-operator Modify spellings for playervar augmented assignments from byte-quoted artifacts first (+= -= *= and any the oracle renders as Modify Player Variable; operators the oracle does NOT support via Modify — per actual behavior e.g. /= //= %= if un-evidenced — stay on the current Set (...) path with a recorded note); fix the HIR->WIR lowering so playervar augmented assignments produce ModifyPlayerVariable for the oracle-evidenced op set, mirroring the already-byte-equal global path (shared by native and adapter-driven lowering; HIR unchanged; differential/PARITY_CASES 8 unaffected); add the playervar augmented-assignment family to the closure scan (roundtrip fixed-point). Regression: global g += 1 unchanged; pixelart normalizedEqual True; 7 settings sections; v1-gates 6/6; PARITY_CASES 8; 12-program matrix; oracle 21/21; adapter 23/23; all suites; clippy/fmt; six CI jobs green; inventory refreshed. The eventPlayer.getCurrentHero() probe is closed as outside-surface (catalog-data gap, unknown-enum-member precedent — deferred data change, not class 3).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions