Skip to content

Commit d27dc33

Browse files
hyperpolymathclaude
andcommitted
docs: 2026-04-19 session closure — typed-wasm L10 CLI + codegen fixes
Bot-primary A2ML + human AsciiDoc companion for the single-session unit of work that landed three codegen fixes (35c476d) and the verify-boundary CLI subcommand + verify-bridge exit-code fix (f6089a2). STATE.a2ml session-note-2026-04-19-a summarises both commits; the history/ docs record the reasoning trace, test evidence, and what the change unblocks downstream. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f6089a2 commit d27dc33

3 files changed

Lines changed: 262 additions & 1 deletion

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
[metadata]
55
project = "affinescript"
66
version = "0.1.0"
7-
last-updated = "2026-04-12"
7+
last-updated = "2026-04-19"
88
status = "active"
9+
session-note-2026-04-19-a = "CODEGEN BUG FIX + TYPED-WASM LEVEL 10 CLI SURFACE CLOSED. (1) lib/codegen.ml commit 35c476d — three fixes: (a) PatCon-with-args stack imbalance in gen_pattern: was LocalTee match_result + LocalGet match_result around stack-neutral bind_fields; removed save/restore, I32Eq sits on stack directly. Fixes 'expected 1 elements on the stack for fallthru, found 2' on match-in-enum returning distinct zero-arity or arg constructors across arms. (b) ExprVar falls back to ctx.variant_tags when lookup_local misses, so bare `Initialised` (parens omitted) resolves as the variant tag — parser accepts the form, codegen previously failed with UnboundVariable. (c) ctx.struct_layouts + ctx.fn_ret_structs: struct layouts registered globally from TopType(TyStruct), propagated to function parameters via p_ty, call-result lets via fn_ret_structs, let-bindings with sl_ty annotation, let-from-let passthroughs. Fixes the .field_1_or_later=0 bug on struct function parameters. struct_name_of_ty handles TyCon / TyApp / TyOwn / TyRef / TyMut wrappers. (2) bin/main.ml commit f6089a2 — new verify-boundary CALLEE CALLER subcommand: compiles two .affine source files, extracts callee's ownership-annotated export interface, runs Tw_interface.verify_cross_module on the caller, exits 0/1 correctly. Shared compile_to_wasm_module helper factored out of verify_file. Fixed verify_boundary_fn exit-code bug (verify-bridge handler): was always returning Ok () even on violations; now tracks violation count and maps to Error so CI catches boundary drift. Level 10 cross-module boundary verifier now has public CLI surface. Evidence: 177/177 tests green unchanged; affinescript-deno-test smoke suite 7/7 (up from 4/4) with new codegen_regression_test.affine; double-track-browser extension_lifecycle_test.affine pilot 10/10 without tagged-struct workaround. Scope note: AffineScript use A.B imports inline at AST layer, so caller from AffineScript source today produces no cross-module WASM imports — new CLI immediately useful for hand-assembled / bridge-pattern callers (IDApTIK bridges, composition tools). Broader applicability gated on cross-module WASM import emission, a separate compiler feature."
910
session-note-2026-04-12-c = "STAGE 12 (SECOND IDAPTIK SCREEN) IN PROGRESS — CharacterSelectScreen chosen as dogfood target (6 class cards: Assault/Recon/Engineer/Signals/Medic/Logistics, 1 confirm → JessicaCustomise). Plan: (1) lib/tea_cs_bridge.ml — new Wasm module, same API surface as tea_bridge.ml, 7 msgs (0-5=select class, 6=confirm), update: selected_tag=msg+1, selected_tag 1-6=class highlighted, 7=navigate. (2) lib/dune + bin/main.ml — add cs-bridge subcommand and update which_arg/interface/verify-bridge. (3) IDApTIK: AffineTEARouter.js+.res add screenJessicaCustomise=6. (4) CharacterSelectScreen.res: csTeaBridge module-level ref, card click handlers → teaDrive, applyView syncs Wasm→ReScript selectedClass, confirm → AffineTEA.update+navigate via showScreenWithTag. Seam check: push TitleScreen→CharacterSelect, select class, confirm→JessicaCustomise, back-stack correct. 173/173 tests currently passing."
1011
session-note-2026-04-12-b = "STAGES 7-11 COMPLETE (173/173 tests). Stage 9: per-path min/max linearity (count_uses_range, LinearDroppedOnSomePath, fn_push else-branch fix). Stage 10: tw_interface.ml multi-module boundary verifier (LinearImportCalledMultiple, LinearImportDroppedOnSomePath, CLI interface+verify-bridge cmds). Stage 11: source-level Cmd linearity — Cmd[ClickMsg] bracket syntax, quantity_of_ty_annotation returns QOne for Cmd[_], infer_usage_block declares QOne locals before subsequent stmts so uses tracked, fires LinearVariableUnused when Cmd dropped; builtins cmd_none+cmd_perform in typecheck/resolve/interp. Commit b8a0f9c."
1112
session-note-2026-04-12-a = "STAGE 6 (CADRE ROUTER) COMPLETE: lib/tea_router.ml generates Wasm 1.0 router module (915 bytes, WebAssembly.validate=true). RouterModel at offset 64: screen_w(+0), screen_h(+4), stack_len(+8), popup_tag(+12), stack_data[8](+16). 11 functions: init, push(Linear), pop, present_popup(Linear), dismiss_popup, resize(Linear×2), get_screen_w/h/stack_len/stack_top/popup_tag. affinescript.ownership: 3 functions with Linear params (push, present_popup, resize). affinescript.tea_layout: version=1, 5 fields. router-bridge subcommand added. 8 new E2E tests; 113 total, 0 regressions. IDApTIK: AffineTEARouter.js + AffineTEARouter.res; Navigation.res: routerBridge field + setRouterBridge + showScreenWithTag + presentPopupWithTag + dismissPopup router sync + resize router sync; TitleScreen.res: teaDrive uses showScreenWithTag/presentPopupWithTag, router.wasm loaded on first show alongside titlescreen.wasm, graceful fallback. router.wasm committed to idaptik/public/assets/wasm/. Both repos pushed. commit: affinescript 5969b7b, idaptik e3eafd1."
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Machine-readable session summary: typed-wasm Level 10 CLI closure +
3+
# three codegen bug fixes. Bot-primary doc per A2ML standard; see the
4+
# .adoc companion for human narrative.
5+
6+
[metadata]
7+
session-date = "2026-04-19"
8+
triggered-by = "double-track-browser extension_lifecycle_test.affine pilot (web-ecosystem/double-track-browser/tests/affine/extension_lifecycle_test.affine)"
9+
scope = "single-session"
10+
closed-by = "Claude Opus 4.7 (1M context)"
11+
author = "Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>"
12+
13+
[summary]
14+
headline = "Three codegen bugs fixed + typed-wasm Level 10 cross-module boundary verifier gains public CLI surface"
15+
state-before = "affinescript-deno-test harness could not run enum+match or multi-field struct tests; Tw_interface.verify_cross_module analysis was complete but only reachable via verify-bridge which hardcodes the two IDApTIK bridges; verify-bridge claimed 'Exit 1 = violations found' but always exited 0"
16+
state-after = "all three codegen idioms work; user-authored .affine CALLEE + CALLER pair can be passed through verify-boundary; verify-bridge exit code propagates correctly"
17+
18+
[changes.codegen.stack-imbalance]
19+
file = "lib/codegen.ml"
20+
function = "gen_pattern"
21+
pattern = "PatCon"
22+
severity = "high"
23+
symptom = "WebAssembly.compile() rejects output with 'expected 1 elements on the stack for fallthru, found 2' on any match arm body that produces an i32 result, when the match scrutinee is an enum and at least one arm pattern has sub-patterns"
24+
root-cause = "tag-test boolean was saved via LocalTee on a fresh __match_result local then re-pushed via LocalGet after stack-neutral bind_fields code; the enclosing If expected ONE i32 but saw TWO"
25+
fix = "remove the LocalTee save and trailing LocalGet restore; bind_fields is net-zero on the stack (LocalGet +1, I32Load 0, LocalSet -1) so the I32Eq result from tag_test stays on top of the stack through to the pattern result"
26+
commit = "35c476d"
27+
28+
[changes.codegen.bare-variant]
29+
file = "lib/codegen.ml"
30+
function = "gen_expr"
31+
pattern = "ExprVar"
32+
severity = "medium"
33+
symptom = "Bare zero-arity enum constructor used as an expression (e.g. `Initialised` with no parens, as a match-arm body or let-rhs) failed with UnboundVariable even though the parser accepts it"
34+
root-cause = "ExprVar only tried lookup_local; the parallel path for ExprCall-with-variant-tag already existed at line 658 but wasn't mirrored"
35+
fix = "ExprVar falls back to ctx.variant_tags when lookup_local misses, emitting I32Const with the variant's tag — matching the semantics of ExprVariant"
36+
commit = "35c476d"
37+
38+
[changes.codegen.param-struct-field]
39+
file = "lib/codegen.ml"
40+
functions = ["StmtLet", "gen_function", "gen_decl/TopFn", "gen_decl/TopType"]
41+
severity = "high"
42+
symptom = "`s.field_1_or_later` on a function parameter `s: State` returned 0 regardless of actual value; `s.tag` (field 0) worked because field 0 was the default offset"
43+
root-cause = "the per-variable field_layouts map was only populated by let-bindings whose RHS was an ExprRecord literal; function parameters, call-result lets, and type-annotated lets defaulted the offset to 0"
44+
fix = "added ctx.struct_layouts (type-name keyed) registered from TopType(TyStruct); added ctx.fn_ret_structs (function-name keyed) seeded from fd_ret_ty; propagated to (a) function params via p_ty, (b) call-result lets via ExprApp(ExprVar, _), (c) let-bindings with sl_ty annotation, (d) let-from-let passthroughs via the RHS's tracked layout; added struct_name_of_ty helper recognising TyCon, TyApp, and ownership-wrapper variants (TyOwn/TyRef/TyMut)"
45+
commit = "35c476d"
46+
47+
[changes.cli.verify-boundary]
48+
file = "bin/main.ml"
49+
subcommand = "verify-boundary"
50+
args = "CALLEE CALLER (positional, both required)"
51+
behaviour = "Compiles both .affine files through the full frontend pipeline via shared compile_to_wasm_module helper (factored out of verify_file). Extracts the callee's ownership-annotated export interface from its affinescript.ownership custom section. Runs Tw_interface.verify_cross_module(iface, caller). Returns `Ok () on empty error list, `Error (false, count) on violations. Manpage documents LinearImportCalledMultiple and LinearImportDroppedOnSomePath with worked example."
52+
scope-note = "AffineScript's use A.B imports inline at AST layer — a caller compiled from AffineScript source today produces no cross-module WASM imports for the verifier to check. New CLI is immediately useful for hand-assembled / bridge-pattern callers (IDApTIK bridges, composition tools) and pre-wired for AffineScript-to-AffineScript once the compiler gains cross-module WASM import emission."
53+
commit = "f6089a2"
54+
55+
[changes.cli.verify-bridge-exit-code]
56+
file = "bin/main.ml"
57+
function = "verify_boundary_fn"
58+
severity = "medium"
59+
symptom = "verify-bridge documentation claimed 'Exit 1 = violations found' but the function always returned `Ok () even when verify_cross_module reported violations"
60+
fix = "introduce total_violations ref; accumulate List.length errs across each bridge checked; at end, if total_violations > 0 return `Error (false, msg) so Cmdliner maps to exit 1"
61+
impact = "CI pipelines calling `affinescript verify-bridge --which all` now fail correctly on boundary drift — previously they silently passed"
62+
commit = "f6089a2"
63+
64+
[commits]
65+
affinescript-repo = [
66+
"35c476d fix(codegen): enum-in-match stack imbalance + param struct-field reads",
67+
"f6089a2 feat(cli): verify-boundary subcommand + verify-bridge exit-code fix",
68+
]
69+
nextgen-languages-submodule-bumps = [
70+
"61f69e5 bump affinescript: fix enum-in-match + param struct-field codegen",
71+
"24a3205 bump affinescript: verify-boundary subcommand + exit-code fix",
72+
]
73+
developer-ecosystem-repo = [
74+
"b7fbe5f fix(affinescript-deno-test): document codegen-bug resolution + regression tests",
75+
]
76+
77+
[tests]
78+
affinescript-alcotest = "177/177 green (unchanged from pre-session)"
79+
affinescript-deno-test-smoke = "7/7 green (was 4/4; new codegen_regression_test.affine added 3 cases)"
80+
double-track-browser-pilot = "10/10 green (previously used tagged-struct workaround; now would pass with idiomatic enum+match form too)"
81+
verify-boundary-cli-smoke = "exits 0 on (callee.affine with pub fn consume(own Int) + caller_empty.affine with no imports) — happy path validated end-to-end"
82+
verify-bridge-cli-smoke = "exits 0 on --which all (two clean internal bridges) — confirms exit-code fix doesn't regress clean case"
83+
84+
[scope-not-closed]
85+
affinescript-cross-module-wasm-imports = "separate feature — `use A.B` currently inlines at AST layer rather than emitting WASM import directives. Unblocks Bigger applicability of verify-boundary to AffineScript-native modules. Not a typed-wasm gap."
86+
87+
[follow-ups-enabled]
88+
ts-to-affinescript-migration = "AI-WORK-todo.md §3c unblocked; remaining 17 files can now use idiomatic enum+match + multi-field structs without workarounds"
89+
double-track-browser-refactor = "tagged-struct workaround in extension_lifecycle_test.affine can be converted to idiomatic enum Lifecycle { ... } form if desired (optional)"
90+
affinescript-deno-test-harness-scale = "v0.2.0 MVP constraints removed; harness ready to scale beyond double-track-browser pilot"
91+
92+
[provenance]
93+
triggering-context = "User asked to lift the affinescript-deno-test harness by fixing the two codegen bugs noted in its README. During investigation, three bugs surfaced (two blocked harness scaling, one blocked bare-constructor usage in match bodies). Scope then extended to 'close typed-wasm Level 10 cross-module' based on user decision after survey of typed-wasm state."
94+
reasoning-trace = "Bugs reproduced with minimal .affine fixtures. Fix-forward on each: stack-imbalance diagnosed via instruction-level trace of PatCon codegen; bare-variant diagnosed by comparing parser acceptance vs codegen ExprVar lookup; param-struct-field diagnosed by inspecting field_layouts population paths. CLI wiring closed the existing Tw_interface analysis (which was already unit-tested at 7 Alcotest cases) into a user-facing subcommand and fixed an orthogonal exit-code bug discovered during review."
95+
96+
[safe-to-close]
97+
yes = true
98+
checklist = [
99+
"affinescript compiler tests 177/177 green",
100+
"deno-test harness 7/7 green",
101+
"double-track-browser pilot 10/10 green",
102+
"all commits pushed to origin (GitHub)",
103+
"STATE.a2ml session-note-2026-04-19-a added",
104+
"KNOWN-ISSUES.md flipped to RESOLVED with root-cause summary",
105+
"affinescript-deno-test README.adoc flipped to RESOLVED",
106+
"AI-WORK-todo.md §11 flipped to RESOLVED with full evidence",
107+
"human-readable companion .adoc in docs/history/",
108+
]

0 commit comments

Comments
 (0)