-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSTATE.a2ml
More file actions
240 lines (224 loc) · 80.1 KB
/
Copy pathSTATE.a2ml
File metadata and controls
240 lines (224 loc) · 80.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# SPDX-License-Identifier: MPL-2.0
# STATE.a2ml — Project state checkpoint
[metadata]
project = "affinescript"
version = "0.1.1"
last-updated = "2026-05-31"
status = "active"
authoritative-status-doc = "docs/CAPABILITY-MATRIX.adoc"
drift-flag = "STALE as of 2026-05-23 PM: this file's [components]/[features]/[project-context] still predate landed PRs since 2026-05-19. It MIRRORS, it does not LEAD. Authoritative sources by topic — readiness: docs/CAPABILITY-MATRIX.adoc; spine + AS↔typed-wasm contract: docs/ECOSYSTEM.adoc; coordination ledger / critical path: docs/TECH-DEBT.adoc; test taxonomy + PR-level gates: docs/standards/TESTING.adoc (added 2026-05-23); panic-attack SOP: docs/standards/PANIC-ATTACK.adoc (added 2026-05-23). Gate baseline: CAPABILITY-MATRIX records 260/260 at 2026-05-19 reconstruction; subsequent borrow-checker work has lifted it (#240 → 263, return-escape → 271/274, &mut surface → 278/281). The exact live number for any given commit comes from `dune runtest --force` — do not hard-code it here. (DOC-05, issue #176.)"
session-note-2026-05-26-publish-104 = "ISSUE #104 CLOSED — FIRST NPM PUBLISH LANDED. @hyperpolymath/affine-vscode@0.1.0 is now on registry.npmjs.org. Owner-action sequence completed today: (1) npm org `hyperpolymath` created on free public-package tier; (2) Granular Access Token generated for scope @hyperpolymath/* with Read+Write, uploaded to repo secret NPM_TOKEN; (3) signed annotated tag affine-vscode-v0.1.0 pushed at origin/main (RSA key 9639451754496E51D6B537CAD119017EBF695AB1); (4) .github/workflows/affine-vscode-publish.yml ran green — `npm publish --access public` succeeded; (5) `npm view @hyperpolymath/affine-vscode` resolves. Downstream consumers (my-lang#66, standards#160) which un-vendored their adapters on 2026-05-21 now have a working `npm install` path; vscode-smoke workflow (skipped per #381 while package was unpublished) will start exercising the live package on its next PR run. Lineage for future @hyperpolymath/* publishes: org+token are reusable; mirror `affine-vscode-publish.yml`'s shape (tag trigger, version-match guard, .npmrc write from secret, npm publish --access public). NPM_TOKEN rotation advised post-publish (token value transited a session transcript during wire-up); see .machine_readable/6a2/PLAYBOOK.a2ml [npm-publish] for the runbook."
session-note-2026-05-30 = "DOC-16 + DOC-17 — DOC-TRUTHING MONITOR FULLY MECHANICAL (issue #176). Two complementary in-repo guards now enforce the doc-truthing rules that were previously external-bot-only. DOC-16 (PR #476, tools/check-doc-truthing.sh): banner-PRESENCE invariant — fails if any over-claiming doc loses its CAPABILITY-MATRIX banner, if the matrix stops self-declaring primacy / loses its 'What AffineScript is NOT' section, or if STATE.a2ml drops its mirror keys; deliberately does NOT phrase-scan (a naive grep false-positives on the negating banners + future-roadmap text). DOC-17 (PR #475, tools/check-doc-overclaims.sh + tools/doc-overclaims.allow): the phrase-detection complement — a frozen-baseline RATCHET over README + docs/** (excluding CAPABILITY-MATRIX + TECH-DEBT, which quote the rule) that fails any NEW backend-breadth / 'production-ready' / stdlib-% occurrence beyond the 13-line baseline (all current entries legit: future-tense roadmap milestones + dated history snapshots + corrective banners); re-baseline via `--update` / `just doc-overclaims-bless`, diff = audit trail. Both wired into `just check` (guard recipe) + ci.yml build job, both toolchain-free bash (run without OCaml). #475 was originally a superset of #476 (built in parallel, same session); on discovering #476 had merged first, #475 was reworked to drop the duplicated banner check and keep only the unique ratchet, renamed check-doc-truth.sh→check-doc-overclaims.sh to avoid the name clash (DOC-DEDUP-clean). FOLLOW-UP (same day, post-#475-merge): the two near-identically-named guards were consolidated so neither lingers — the ratchet (incl. `--update` mode + tools/doc-overclaims.allow baseline) was folded INTO tools/check-doc-truthing.sh using #476's fail-accumulator/note() structure, and tools/check-doc-overclaims.sh was deleted. A SINGLE toolchain-free guard now enforces both presence invariants (DOC-04/05) and the over-claim ratchet (DOC-08/09) in one run; one CI step; the `guard` recipe runs it; re-baseline via `just doc-truth-bless`. DOC-17 folded into DOC-16 in the ledger. Gate-number policy unchanged (DOC-05): live `dune runtest --force` count never hard-coded. No compiler code touched. Refs #176 / #175."
session-note-2026-05-31-partial-port-488 = "RES-TO-AFFINE PARTIAL-PORT MODE #488 SLICES 1-3 (successor to closed #57). New --partial flag, a DISTINCT model from --translate: renders module-top-level functions (let f = (params) => body) into AffineScript fn skeletons whose output DELIBERATELY does not type-check (un-annotated ReScript fns can't yield a compilable fn) but DOES parse — un-inferable types are `_` holes, un-translatable expr/pattern are () /* TODO */ / _ /* TODO */ islands. (1) Slice 1 (#494 / c157a0f): fn skeletons + switch->match (variant/tuple/literal patterns) + expression translation (literals / idents / calls / binary-ops with float-op +.->+ and identity-equality ===->== normalisation / ++ concat / member + module-qualified access / ternary). The binary operator is an anonymous tree-sitter token, sliced from source between the operands. (2) Slice 2 (#495 / 78906f9): pipe-first -> desugaring (a->f(b) -> f(a, b); chains are left-nested so x->f->g(2) -> g(f(x), 2) falls out of the recursion); if/else; blocks with let statements. (3) Slice 3 (#496 / 4d4d1d4): array literals ([a, b] -> [a, b]) + record literals ({x, y} -> Rec #{ x: x, y: y }; AffineScript records are NOMINAL so an anonymous record gets the placeholder type Rec for the human to rename; field punning {x} -> x: x). Walker-internal: translate_expr / translate_switch / translate_pipe / translate_if / translate_as_block / translate_block(_inner) / translate_block_let (mutually recursive) + translate_pattern + partial_function + collect_partial + Walker.translate_partial; emitter emit_partial; main --partial (precedence over --translate, walker-only). 32 res-to-affine walker tests. VERIFIED LOCALLY each slice (apt-bootstrapped toolchain): full dune build exit 0; dune runtest green; main.exe check on the generated skeletons reaches resolution/type-checking WITHOUT a parse error (the partial-port bar — type/resolution errors are expected). REMAINING #488: JS objects / interpolated template strings, labelled-arg refinement, combining --partial with --translate (declarations + functions in one pass), and module-qualified-reference RESOLUTION (a ReScript->AffineScript module-mapping POLICY decision, deliberately not built without owner scoping). #488 stays OPEN. Refs #488 / #57 (closed)."
session-note-2026-05-31-migration-phase3-slice3-and-closure = "MIGRATION-ASSISTANT PHASE 3 SLICE 3 + #57 CLOSURE + SUCCESSOR #488. (1) PR #484 (squash ea8bef5): res-to-affine --translate extended to module-level literal value bindings — let <id> = <literal> lowered to a typed AffineScript const name: T = value; for int/float/string/bool (AffineScript has no module-level let; a top-level value binding is const, annotation + semicolon required). New walker.ml helpers classify_number / translate_literal / translate_let_const + a let_declaration branch in collect_translations. Conservative: call bodies, ref(...) mutable-globals, destructuring patterns, and exotic numbers (hex/octal/binary/signed/scientific/underscored) are skipped — number forms limited to plain decimal int and D+.D+ float. New fixture test/fixtures/phase3c.res + 4 gated walker tests (walker-phase3c-let-const). (2) VERIFIED LOCALLY (apt-bootstrapped toolchain): full dune build exit 0; 22 res-to-affine walker tests green against pinned grammar 990214a; compiler oracle main.exe check on the generated consts returns Type checking passed. (3) #57 CLOSED as completed (owner-directed) — the declaration-translation goal is delivered across slices 1 (#477 / 2763909) / 2 (#481 / 3484010) / 3 (#484 / ea8bef5) plus the session-start toolchain hook (#482 / 138baac); --translate now renders every self-contained top-level declaration (type aliases, sums, structs, generics, literal consts) to compiler-verified AffineScript. (4) SUCCESSOR #488 filed: res-to-affine partial-port mode (switch->match / function translation) + module-qualified-reference resolution — both OUT of the every-emitted-form-type-checks model by construction (switch->match is an expression needing whole-function translation of usually-un-annotated ReScript fns, which AffineScript fn signatures can't type-check; qualified refs parse post-#228 but won't resolve without a module-mapping story). #488 is a child of estate migration epic #406; remaining migration work continues there. Refs #57 (closed) / #488 / #406."
session-note-2026-05-30-migration-phase3 = "MIGRATION-ASSISTANT PHASE 3 SLICES 1+2 (issue #57) + SESSION-START HOOK. (1) res-to-affine --translate now turns fully-structural ReScript type declarations into compiler-verified AffineScript; two slices landed off main. Slice 1 (PR #477, squash 2763909): primitive type aliases (type userId = int -> type UserId = Int) and simple sum types (type color = Red | Green -> type Color = | Red | Green; payloads mapped, Circle(float) -> Circle(Float)). Slice 2 (PR #481, squash 3484010): record types -> AffineScript struct (type point = {x: int} -> struct Point { x: Int }) and GENERICS — ReScript type params 'a mapped to AffineScript [A], threaded through aliases, sums, and records (param refs in bodies map 'a -> A). (2) New code is walker-internal (tools/res-to-affine/walker.ml): translate_type_ref / translate_variant / translate_record_fields / affine_type_param / extract_type_params / translate_type_binding / collect_translations, plus Walker.translate and Emitter.emit_translation; CLI gains --translate (walker-only; no-op under --engine=scanner). Detection keys on tree-sitter node TYPE not field labels, robust to the grammar's optional alias/body-field ambiguity. (3) CONSERVATIVE BY CONSTRUCTION — a decl translates only when every part is representable; qualified-path RHS, non-primitive/opaque refs, nested generics (array<int>), object types, GADT returns, variant spreads, and records with mutable or optional-? fields are SKIPPED (left in the marker block + quoted original), never mis-translated. Two normalisations keep output referenceable: lower-case type names capitalised (color->Color) and type vars mapped ('a->A), because lib/parser.mly reads a lower-case name in type position as a TyVar not a TyCon. (4) QUALIFIED-PATH RHS DEFERRED: Belt.Map.t now PARSES (the #228 grammar gap closed — parser.mly:515 wires qualified_type_name into type_expr_primary) but Belt::Map::T would not RESOLVE against a non-existent target module, which would break the every-translated-form-type-checks guarantee; it waits for a module-mapping story. Remaining Phase-3 slices: let->const for literal bindings, switch->match (needs body translation), qualified-path resolution. #57 stays OPEN. (5) VERIFICATION: unlike earlier res-to-affine work this was built+tested locally — the OCaml toolchain was apt-bootstrapped (opam.ocaml.org is network-blocked here, but Ubuntu packages match the project pins: dune 3.14 / cmdliner 1.2 / alcotest 1.7 / menhir 20231231 / sedlex 3.2 / ppx_deriving 5.2 / ppxlib 0.32 / yojson 2.1 / js_of_ocaml 5.6). Full dune build exit 0; dune runtest green (21 res-to-affine cases incl. gated walker tests against pinned grammar 990214a); and the COMPILER ORACLE (main.exe check, the #228 method) returns Type checking passed on every generated form. (6) CONFIG: added .claude/hooks/session-start.sh (SessionStart hook) registered in .claude/settings.json so future web sessions auto-provision this exact apt toolchain + tree-sitter CLI + pinned grammar — synchronous, idempotent, best-effort, web-only (CLAUDE_CODE_REMOTE guard); settings.json also gains dune/tree-sitter build+test permissions. (7) Both #477 and #481 were admin-merged while CI runners were backlogged; #481 was fully locally verified before merge. Refs #57 / #228."
session-note-2026-05-26 = "MIGRATION-ASSISTANT PHASE 2C + REPO-TIDY STACK (T-1..T-7) + STDLIB BLOCKER CLOSURES. (1) PR #357 — feat(res-to-affine) Phase 2c on branch claude/epic-gauss-Mbi0E: tree-sitter walker extended from #322's single Side_effect_import detector to all six anti-patterns. New detectors in tools/res-to-affine/walker.ml — detect_raw_js (any extension_expression node), detect_untyped_exception (try_expression / call to value_identifier \"raise\" / member_expression starting with Js.Exn or ending with Promise.catch), detect_mutable_global (top-level let_declaration whose body is call to value_identifier \"ref\", OR top-level mutation_expression), detect_inline_callback_record (>=3 inline function values in a record literal or a call_expression's arguments list — handles direct function children + labeled_argument + record_field wrappers), detect_oversized_function (function node whose stop.row - start.row + 1 > 50). Module-toplevel predicate refactored to a single at_module_toplevel helper that walks the ancestor chain refusing on `function` or `let_binding` body. Findings deduped by (kind, line) so the AST walker doesn't emit more bullets than the line-based scanner. CLI flipped --engine=walker to the default in tools/res-to-affine/main.ml (scanner remains as fallback when grammar / tree-sitter CLI missing — pre-existing graceful-fallback path from #322 unchanged). Scanner.kind extended with Inline_callback_record + Oversized_function variants; scanner.ml gives them labels + guidance. New fixture test/fixtures/phase2c.res exercises the two walker-only kinds. test_walker.ml grew per-kind tests under three new suites (walker-side-effect-import / walker-phase2c-parity / walker-phase2c-new-kinds). README + walker.mli updated. (2) Phase 2c CI fix push (e7a3a44): initial Phase 2c commit had build+lint failing; defensive rewrite replaced the labeled-only `mk_finding ~kind:K ~line:L ~excerpt:E :: acc` emit pattern with explicit `let finding : Scanner.finding = { ... } in finding :: acc` matching Phase 2b's working style, plus replaced non-ASCII glyphs in comments (≥ → >=, … → ...) defensively. (3) DIAGNOSED: `main` itself is red. PR #359 (T-1, pure file-rename + delete + one-line CONTRIBUTING.md edit, zero OCaml touched) also fails on build+lint. That conclusively shows the build failure is inherited from main, not introduced by Phase 2c or any tidy work. Matches CLAUDE.md §\"CI signal reliability\" — auto-merge fires even when build is red; the historical pattern of #334/#335/#336/#344 landing red applies. Root cause on main is NOT diagnosed in this session (needs the actual `dune build` log; WebFetch on the actions UI returns React skeletons, not log content; container has no OCaml toolchain to repro locally). Filed implicit follow-up: someone with shell access should run `gh run view --log-failed <run-id>` against any of the recent failing runs to identify the underlying lib/ compile error. (4) REPO-TIDY STACK (T-1..T-7) — six small PRs landed off origin/main, each reviewable in isolation, none touching .ml/.mli, all inheriting the same baseline build/lint failure as PR #359 (proof above). T-1 PR #359 *MERGED* — AI.a2ml → 0-AI-MANIFEST.a2ml (Hypatia root_hygiene rule + sibling-repo convention), delete AI.djot (superseded), docs/TECH-DEBT-alt.adoc → docs/TECH-DEBT.adoc (restoring the canonical name every cross-link in the repo already points at; PR #356's -alt suffix during the #351 split is dead weight post-#355). T-2 PR #360 — delete 2,182 lines of submarine-game docs (DAMAGE-SYSTEM.md, CONTROLS-REFERENCE.md, GAME-BUNDLING-STRATEGY.md), zero cross-references. T-3 PR #365 — 13 loose root .md/.adoc moved into docs/ subtree (ABI-FFI-README → docs/reference/ABI-FFI.md, ALPHA-1-RELEASE-NOTES → docs/history/, BACKEND-{ANALYSIS,IMPLEMENTATION} → docs/architecture/, COMPILER-CAPABILITIES → docs/reference/, EXPLAINME/KNOWN-ISSUES/NAVIGATION/PROOF-NEEDS/ROADMAP → docs/, LICENSING-GUIDE/SECURITY-SETUP → docs/governance/, RSR_OUTLINE → docs/standards/RSR-OUTLINE.adoc), two new subdirs docs/architecture/ + docs/reference/, cross-refs fixed in CAPABILITY-MATRIX / BACKEND-IMPLEMENTATION / CONTRIBUTING / NAVIGATION / res-to-affine; root drops 17→5 community-health files. T-4 PR #366 — RSR_COMPLIANCE.adoc at root with four documented deviations (no guix.scm/flake.nix; STATE.scm substituted by .machine_readable/6a2/STATE.a2ml because .scm is reserved for Guix per language policy; 7 TS exemptions; 2 runtime exemptions — all cite CLAUDE.md as authoritative). T-5 PR #367 — wiki/README.md rewrite (drops ~20 dead links to non-existent .md files, updates stale ../ROADMAP.md → ../docs/ROADMAP.adoc path, annotates Features-at-a-Glance examples with current maturity, switches Ownership example from &File/&mut File sigil syntax to canonical ref File/mut File keyword types) + parse-only honesty banner on wiki/language-reference/dependent-types.md + partial honesty banner on wiki/language-reference/traits.md. T-6 — direct issue triage, no PR: closed #246 (ESC-02 JSON.t — stdlib/json.affine delivers Json ADT + encoders/decoders/get_field/stringify) and #247 (ESC-03 Dict.t — stdlib/dict.affine delivers empty/from_pairs/get/contains/size/insert/set/remove/keys/values), both with confirmation comments referencing the LANDED status in docs/TECH-DEBT.adoc STDLIB-02/03 rows. T-7 this PR — adds this session-note + the T-1..T-7 ledger entry below in docs/TECH-DEBT.adoc. (5) ALSO RESOLVED EARLIER IN SESSION (pre-T-6, as part of addressing the sustainabot tracker blockers the user asked about): #161 (Json) and #162 (Dict) closed with explicit owner-author-acknowledged closure comments dated 2026-05-24; this session was the explicit owner request to act on those. Migration-assistant tracker #57 stays open — Phase 2c lands #322's Phase 2b → 2c walker work but Phase 3 (partial translation of pure-structural forms — the phase the sustainabot tracker's exit criterion 3 specifically gates on) remains unstarted. (6) OPEN PRs AT SESSION END: #357 (Phase 2c, ready to merge — author/owner discretion on the baseline-red CI), #360/#365/#366/#367 (T-2/T-3/T-4/T-5, same baseline-red situation; pure-rename / pure-delete / pure-doc-add changes, no behavioural risk). (7) NOT DONE IN THIS SESSION: actual root-cause diagnosis of main's red build/lint; ~20 missing wiki pages (installation, hello-world, expressions, errors, package-manager, lsp, formatter, linter, stdlib subpages, design/*); guix.scm or flake.nix addition; full wiki content refresh beyond README + two banners; sync of wiki/ to the GitHub-wiki upstream repo (separate git repo, owner pushes manually). (8) CROSS-REPO: this session is AffineScript-only — the gitbot-fleet sustainabot tracker that prompted #161/#162/#57 lives in a different repo and was not touched here."
typed-wasm-formalisation-2026-05-23 = "AS↔TYPED-WASM INTERFACE FORMALISED + WIDENING ROADMAP + TWO PROPOSED ADRs. (1) docs/specs/TYPED-WASM-INTERFACE.adoc + .a2ml — formal spec of the v1 interface (was previously scattered across ECOSYSTEM.adoc + code comments in lib/codegen.ml, tw_interface.ml, tw_verify.ml). Captures: enforced surface (L7 aliasing + L10 linearity + L13 module isolation negative-form); carrier binary layout (u32 entry_count + per-entry [u32 func_index | u8 param_count | u8[n] param_kind | u8 ret_kind], kind ∈ {0 Unrestricted, 1 Linear, 2 SharedBorrow, 3 ExclBorrow}, all LE, omit-when-empty, no version field in v1); producer machinery file-line pointers (lib/codegen.ml ~110-177 + ctx.ownership_annots ~2312/~2629); consumer machinery (tw_verify.ml 332 LOC, tw_interface.ml 276 LOC, primary entry verify_from_module); CLI surface (verify / verify-boundary / verify-bridge / interface — verify-boundary's pre-2026-04-19 silent-success bug fixed by f6089a2); test coverage (test_tw_isolation.ml 5 cases + E2E Boundary Verify 3 cases + ownership-roundtrip 3 cases; honest gap: no C5.1 cross-compat fixtures shipped); multi-producer responsibilities (AS = ref producer + spec of record until C5.1; ephapax = second producer same format; typed-wasm = spec + Rust verifier + cross-compat target); deliberate non-features NF-001..NF-005 with roadmap anchors; stability promise (v1 frozen; unilateral changes forbidden). (2) docs/specs/TYPED-WASM-ROADMAP.adoc + .a2ml — making typed-wasm a natural + optimal target. Five tranches with unblock graph: A (ergonomics, no ABI change: A1 --typed-wasm gating flag, A2 report-typed-wasm JSON, A3 extract ownership-section to dedicated module, A4 diagnostic span parity) → B (schema hygiene, additive single-byte ABI: B1 = ADR-020) → C (compiler-side foundations for L1–6/L14–16: C1 region inference / CORE-01 Phase 3, C2 session-type+capability tracking, C3 CONV-02/ADR-016 S2..S4 async recogniser, C4 capability-typed externs) → D (multi-producer ABI widening: D1 = ADR-021, D2 affinescript.regions, D3 affinescript.{capabilities,session,choreography}) → E (cross-compat closure: E1 INT-12/CONV-05 fixtures into typed-wasm C5.1, E2 Rust-verifier parity out-of-scope-here). Recommended sequencing: A1→A3→A2→E1→B1→A4→D1→C1..C4→D2/D3→E2. (3) ADR-020 (PROPOSED, META.a2ml): ownership-section schema versioning — v1 unversioned → v2 with 0xAF sentinel + u8 version + entry_count. v1 readers fail cleanly on v2 (bad entry-count parse); v2 readers dispatch on sentinel. Coordinated landing: verifier ships parse first, producers flip emit together. (4) ADR-021 (PROPOSED, META.a2ml): multi-producer ABI coordination model — four axes (spec authority OCaml→Rust on C5.1 closure; coordinated landing protocol; test parity protocol; conflict resolution via serialised queue owned by typed-wasm). ADR-020 is itself the first test of ADR-021's protocol. (5) Cross-links: ECOSYSTEM.adoc §contract paragraph now points at TYPED-WASM-INTERFACE.adoc as authoritative; CAPABILITY-MATRIX.adoc typed-wasm row links to INTERFACE + ROADMAP and notes L13 alongside L7/L10; TECH-DEBT.adoc Stage E section gets header pointer + DOC-13/14/15 ledger entries (interface, roadmap, ADRs); docs/README.adoc + NAVIGATION.adoc list both new specs. (6) Pure documentation work — no compiler code changed, no behaviour change. Both ADRs require owner ratification before they bind."
stage-d-batch-2026-05-23 = "INT-01 ::-in-value-expr + BUG-005 deferred fixture + STDLIB-04 audit. (1) INT-01 follow-up: lib/parser.mly line ~835 — added `upper_ident COLONCOLON lower_ident` production emitting `ExprField(ExprVar Mod, lower_ident)`, the same AST shape `Mod.fn` already produces. Disambiguated from the line-above `Type::Variant` rule by lower_ident vs upper_ident. No resolver change required: Resolve.lower_qualified_value_paths handles both `.` and `::` syntaxes identically because the lowering pattern-matches on the ExprField shape, not the source separator. test/e2e/fixtures/cross_caller_qualified_colon{,_alias}.affine + 2 alcotest cases in qualified_value_tests pin both `use Mod; Mod::fn(x)` and `use Mod as M; M::fn(x)`. The remaining INT-01 'parser gap, not resolver' note in the ledger is now closed. (2) BUG-005 deferred regression test landed (test/test_e2e.ml, wasm_gc_loud_fail_tests): `fn main() -> Int { return totally_undefined_callee(42); }` is fed directly to Codegen_gc.generate_gc_module (no resolve step) and the emitted error must contain the unknown name — which UnboundFunction's format string includes verbatim. Closes the deferred-status entry on the [[closed-bug]] BUG-005 record. (3) docs/STDLIB-EXTERN-AUDIT.adoc — full triage of 135 extern fn + 24 extern type across 11 stdlib files into 4 classes (built-in / typed-boundary-bridge / adapter-Deno / adapter-Node); per-row status (done/partial/stub-only) and unblock condition for each module. STDLIB-04's ledger entry now points here. Implementation PRs close one row at a time; the audit doc is the canonical roadmap, not STATE.a2ml. (4) Not yet verified locally — no OCaml toolchain in this remote execution environment. CI is the verification surface; parser change is low-risk (single production, no Menhir conflict — `upper_ident COLONCOLON lower_ident` is unambiguous against the prior `upper_ident COLONCOLON upper_ident` rule by token class)."
hygiene-uplift-2026-05-23 = "TEST + BENCH + COVERAGE + PANIC-ATTACK SOPs LANDED. (1) docs/standards/TESTING.adoc — authoritative test taxonomy (alcotest unit / alcotest E2E / fixture / golden / smoke / microbench), PR-level expectations, deferred-regression-test protocol. Replaces the stale docs/guides/TESTING-REPORT.adoc (2025-12-29 snapshot, 47/27 pass rate, contradicted by live gate since early 2026); retired in-place with redirect to CAPABILITY-MATRIX + standards/TESTING. (2) bench/ scaffold — dune stanza under @bench alias, 4 phase microbenches (lex/parse/typecheck+quantity/codegen-wasm) over 3 shared fixtures (tiny_arith, medium_struct_match, larger_enum). just bench / just bench-record recipes. CI job `bench-visibility` uploads bench-output.log as artifact. Visibility-only — no merge-blocking threshold. (3) bisect_ppx coverage wiring — added as with-test depend in dune-project + affinescript.opam. just coverage recipe (HTML to _coverage/, gitignored). CI job `coverage-visibility` uploads _coverage as artifact + summary to step output. Visibility-only — no enforced floor. (4) docs/standards/PANIC-ATTACK.adoc — SOP for the estate compliance scanner. Disposition vocabulary (fix/accept/suppress/defer); standing-suppression record format; cross-refs to MAINTENANCE-CHECKLIST. .github/workflows/panic-attack.yml: weekly Sunday 03:00 UTC cron + workflow_dispatch, installs panic-attacker via cargo from road-skate/features/panic-attacker, uploads log as artifact. (5) docs/README.adoc + NAVIGATION.adoc updated — standards/ now lists TESTING + PANIC-ATTACK; guides/TESTING-REPORT marked RETIRED. (6) .gitignore — added /_coverage/, bisect*.coverage, /bench-runs/."
session-note-2026-05-20-pm = "POST-#303 CATCH-UP: #297/#300/#301/#302/#304 + repos-monorepo retirement decision. (1) PR #300 MERGED (sha 8110548) — closed #297. lib/version.ml single source of truth + release.yml sed-bake step. Binary --version self-reports 0.1.1 now. (2) PR #304 OPENED for #301 (repo-wide PMPL→MPL-2.0 SPDX surface sweep, Option 3 of the three in the issue): 673 files / +1127 −711 / 1116 SPDX headers flipped / code generators emit MPL-2.0 / .machine_readable license fields reconciled / dune-project (license …) reconciled from stale MIT-OR-AGPL → MPL-2.0 / affinescript.opam regenerated → license: \"MPL-2.0\" / LICENSES/LICENSE-MPL-2.0 added (canonical Mozilla 373-line text) / root LICENSE narrative preserved unchanged (PMPL still narrated as preferred + MPL-2.0 fallback explanation intact) / vendored sister-repo subtrees (road-skate/ + affinescript-vite/) explicitly excluded. Verification: 295/295 dune tests + 6/6 shim tests + binary 0.1.1 + grep for residual PMPL SPDX → empty (excl. vendored). Side-effect noted in PR body: pre-existing dune-project symlink-to-.build/dune-project got broken by sed atomic-rename; both files now real-file content-identical. (3) Issue #302 CLOSED — owner confirmed GitLab+Bitbucket mirroring is intentionally off; Codeberg + Radicle are the active forges. (4) repos-monorepo RETIREMENT decided: snapshot tarred (23MB gzip / 144MB extracted / 23,970 entries / canonical origin/main shallow-clone / .git excluded) to /mnt/c/Users/USER/Downloads/repos-monorepo-snapshot-2026-05-20.tar.gz; owner-upload-to-Google-Drive then `gh repo delete hyperpolymath/repos-monorepo --yes`. repos-monorepo#9 (nested casket-ssg coherence) becomes moot post-delete since the aggregator that would have carried it is gone — standalone hyperpolymath/casket-ssg already merged casket-ssg#8 + has its own instant-sync.yml for forge propagation. (5) DOC/MEMORY: this session-note + the TECH-DEBT.adoc INT-04 update (mentioned #297/#300 + #301/#304) are part of follow-up PR to #303. The 4 earlier-saved reference memories (macos-13 retired, gitbot Refs-auto-close, estate mirror state, JSR publish recipe) all still apply unchanged."
session-note-2026-05-20 = "INT-10 / #282 CLOSURE + JSR PUBLISH + ESTATE macos-13 SWEEP + DOC/MEMORY HARDENING. (1) ISSUE #282 ACTUALLY-DELIVERED end-to-end. PRs that landed today: #291 (partial pins.js fill for linux-x64 + macos-arm64, shim 0.1.1 — bridging before macos-x64 leg landed); #292 (release.yml: retired macos-13 GH-hosted runner → macos-15-intel — root cause of v0.1.0 macos-x64 leg sitting queued ~10h, traced to actions/runner-images#13046/#13402/#13634 'macos-13 fully unsupported since 2025-12-04'); v0.1.1 tag cut, full 3-platform release (linux-x64/macos-arm64/macos-x64) + SHA256SUMS published; #293 (delete 4 orphan .res files + the affine-res ReScript-bindings package — same precedent as packages/affine-ts/ removal 2026-05-11, fixed the recurring governance Language/anti-pattern check failure on every PR); #294 (release.yml checksums job: pass --repo \"$GITHUB_REPOSITORY\" so `gh release download` doesn't probe a missing .git — bug hidden on v0.1.0 by the macos-13 stall that pre-empted the checksums leg); #295 (final shim closure: pins.js VERSION→v0.1.1, all 3 sha256 fields filled, shim @hyperpolymath/affinescript 0.1.1→0.1.2, SHIM_SPEC bumped lock-step in tools/affinescript-lsp/src/compiler.rs); #298 (cross-runtime refactor: Deno/Bun/Node detection at module load + helper layer for hostOs/hostArch/envGet/readBytes/writeBytes/mkdirRecursive/chmodExec/spawnInherit/thisIsMain — was Deno-only — plus mod.d.ts + triple-slash reference so JSR fast-check finds the types and emits no warning; .claude/CLAUDE.md gained Runtime Exemptions section as the carve-out from the estate-wide Bun/Node ban); #299 (relicense the JSR shim package only — packages/affinescript-cli/{deno.json + 4 SPDX headers} — from MPL-2.0 to MPL-2.0 because JSR validates against the SPDX list and `MPL-2.0` isn't on it; wider repo PMPL→MPL-2.0 deliberately deferred — #301). (2) JSR FIRST-TIME PUBLISH RECIPE walked: claim scope @hyperpolymath → create package record (jsr.io/new — dry-run won't catch this) → link trusted GitHub repo at package or scope settings (OIDC actorNotAuthorized otherwise — dry-run won't catch this either) → SPDX licence → mod.d.ts + triple-slash → cross-runtime tickbox. publish-jsr.yml workflow (manual workflow_dispatch); 6 dispatched runs today before all gates aligned (~7:33 UTC). Verified live: https://jsr.io/@hyperpolymath/affinescript 200; meta.json `latest: 0.1.2`. Real fetch+verify+exec smoke against the v0.1.1 release ran green on linux-x64 (resolveCompiler → SHA-verify → cache → exec `--version`). (3) ESTATE macos-13 SWEEP — sole non-affinescript first-party hits: `casket-ssg#8` (release.yml: `runner: macos-13` → `runner: macos-15-intel`, merged) + `proven#29` (zig-ffi.yml: `os: macos-13` → `os: macos-15-intel`, merged) + `repos-monorepo#9` (nested casket-ssg copy in boj-cartridges/polystack/poly-ssg/casket-ssg/.github/workflows/release.yml — still open as of writing, coherence-only since instant-sync.yml in each repo independently propagates). gh search code confirmed only 3 distinct first-party repo hits across the 360-repo active estate. (4) DISCOVERED — GitLab + Bitbucket mirrors NOT operating: empirical inspection of mirror.yml runs on affinescript / casket-ssg / repos-monorepo all `completed/skipped`; `GITLAB_MIRROR_ENABLED` / `BITBUCKET_MIRROR_ENABLED` repo vars unset; `GITLAB_TOKEN` / `BITBUCKET_TOKEN` secrets absent (only FARM_DISPATCH_TOKEN present); gitlab.com probes 302, bitbucket.org probes 404. repos-monorepo DOES have CODEBERG_MIRROR_ENABLED + RADICLE_MIRROR_ENABLED set → active forges are Codeberg + Radicle + whatever `.git-private-farm` fans out to. Filed as open question: #302. (5) FOLLOW-UPS: #297 (version-string drift — bin/main.ml/lib/repl.ml/lib/lsp_server.ml/lib/onnx_codegen.ml/dune-project all hardcoded `0.1.0` even on the v0.1.1 binary; fix PR #300 introduces lib/version.ml single-source-of-truth + release.yml sed-bake step on every tag); #301 (wider PMPL→MPL-2.0 estate-relicense decision needed); #302 (mirror status — intentional-off or unfinished-setup). (6) DOCUMENTATION: this file (a session-note); docs/PACKAGING.adoc (status table for all 3 JSR-publishable packages + first-publish-gotchas section); docs/TECH-DEBT.adoc (INT-04 / INT-10 lines updated to reflect actual publish). Memory: reference_macos_13_runner_retired.md, reference_estate_gitbot_auto_closes_on_refs.md, reference_estate_mirror_state_2026_05_20.md, reference_jsr_publish_recipe.md all saved + indexed in MEMORY_STANDING.md."
session-note-2026-05-03-c = "EXTERN/VSCODE/ARRAY/PATCON BATCH. (1) `extern fn name(...) -> Ret;` and `extern type Name;` now parse — added EXTERN keyword to lexer/token/parse_driver, FnExtern to fn_body and TyExtern to type_body in AST, extern_fn_decl + extern_type_decl rules in parser.mly. Resolve registers the symbol; Typecheck.check_fn_decl special-cases FnExtern to register the polymorphic scheme without body checking; Codegen.gen_decl emits a real `(import \"env\" \"<name>\" (func ...))` for each extern fn, mirroring gen_imports's cross-module shape. Borrow + Quantity skip extern fns. lib/dune now demotes warning 8/9 from error to warning so the new variants don't require lock-step updates across all 27 codegens (any non-Wasm codegen that doesn't handle FnExtern raises Match_failure with file:line at runtime — correct signal for 'this target has no story for host-supplied implementations'). 192 tests; 0 regressions. (2) Issue #35 Phase 2: stdlib/Vscode.affine and stdlib/VscodeLanguageClient.affine ship the ~12 + 3 binding declarations from the issue's API inventory (registerCommand, getConfiguration, showInformationMessage, createTerminal, ...). packages/affine-vscode/mod.js is the JS-side adapter that translates each `extern fn` invocation into the corresponding vscode/lc API call, with a JS-side handle table for opaque host objects and a string-marshal helper that reads `[u32 length][utf-8 bytes]` out of the wasm memory. Adapter returns a namespaced object `{ Vscode: {...}, VscodeLanguageClient: {...} }` matching the WASM cross-module imports' module names. examples/vscode_extension_minimal.affine demonstrates an end-to-end VS Code extension authored in AffineScript using `use Vscode::{registerCommand, showInformationMessage, pushSubscription}` — compiles to .cjs via the Node-CJS path. (3) issues-drafts/02 closed: `[T]` array type now parses via `LBRACKET type_expr RBRACKET → TyApp (Array, [TyArg elem])` in lib/parser.mly's type_expr_primary rule. Verified for fn params, return types, struct fields, and nested `[[T]]`. Other stdlib files (Option/math/io/string) still fail with distinct issues (`fn() -> T` type syntax, `Option<T>` angle brackets) — the array fix advanced math.affine's failure point from line 349 to 354 etc. (4) PatCon sub-pattern destructuring under WasmGC: `match Mk(7, 99) { Mk(a, b) => a }` now lowers to RefCast + StructGet for the tag check + per-field RefCast HtI31 + I31GetS unboxing for each PatVar sub-pattern. Validated end-to-end: emitted .wasm instantiates in Deno and `main()` returns 7. The mixed-arity case (zero-arg + with-args in same enum, e.g. Option) errors loudly with the workaround documented (split into two matches, or use Wasm 1.0). Unifying variant rep — uniform `struct {tag, payload}` so Some+None can share — is the next destructuring milestone. 195 → 198 → 200 tests; 0 regressions throughout."
session-note-2026-05-03-b = "FOLLOW-ON BATCH AFTER TYPED-WASM CLOSURE: variant-with-args under WasmGC, Node-target codegen Phase 1 (issue #35), stdlib Core.affine fix, cross-module for other codegens. (1) lib/codegen_gc.ml — added gen_variant_with_args helper that lowers ExprApp(ExprVariant(_), args) and bare-name `Some(42)` (via variant_tags lookup in ExprApp ExprVar) to a tagged anon-struct allocation [tag: i32, payload: anyref, ...] with i32 args boxed via ref.i31. ExprVar gained variant_tags fallback so `return Happy` works. gen_gc_function now post-processes body_code: when result_vt ≠ I32 the trailing `push_i32 0` fallback (emitted by gen_gc_block when blk_expr=None) is swapped for RefNull HtAny, which fixes 'end[0] expected type anyref, found i32' validator errors on functions that explicitly return a struct. Validated: emitted .wasm instantiates in Node 18 / V8 14, main() returns the GC struct. f64 args remain UnsupportedFeature (no i31 boxing path). (2) lib/codegen_node.ml — new module implementing issue #35 Phase 1 (Node-CJS emit). Wraps Codegen.generate_module output in a CJS shim with: inline base64-encoded wasm constant, lazy WebAssembly.instantiate on first activate/deactivate call, JS-side opaque-handle table (_registerHandle / _getHandle / _freeHandle exported for Phase 2 vscode bindings), minimal WASI fd_write so println-style codegen works, exports.activate/exports.deactivate re-exports of the wasm module's same-named exports. .cjs output extension dispatches in bin/main.ml (both JSON and non-JSON paths). Smoke-tested: real Node 18 require()s a generated .cjs and successfully calls activate(fakeContext) → 0, deactivate() → 0. Issue #35 Phases 2-4 (stdlib/Vscode.affine bindings, extension.ts → extension.affine migration, rattlescript-face sweep) remain as separate work. (3) stdlib/Core.affine — three parser-collision fixes: `pub fn const[A,B]` → `always` (const is a reserved keyword for compile-time bindings); `fn(x: A) -> C { ... }` lambdas → `|x: A|` form (the parser's actual lambda syntax); `flip` rewritten from `(A, B) -> C` to curried `A -> B -> C` to dodge tuple-arrow ambiguity. The originally-blocked tests/modules/test_simple_import.affine (use Core::{min}; min(10,20)) now compiles end-to-end. Other stdlib files (Option/math/io/string/...) still don't parse — each has distinct issues (fn() type syntax, `[T]` array type per issues-drafts/02, `Option<T>` angle brackets) that need their own passes or compiler-level fixes. (4) lib/module_loader.ml — new flatten_imports : t -> program -> program that prepends imported public TopFns into the importer's prog_decls (deduplicating against local fn names). bin/main.ml now binds [let flat_prog = Module_loader.flatten_imports loader prog in] in both compile_file paths and threads flat_prog through all 22 non-Wasm codegens (Julia, JS, C, WGSL, Faust, ONNX, OCaml, Lua, Bash, Nickel, ReScript, Rust, LLVM, Verilog, Gleam, CUDA, Metal, OpenCL, MLIR, Why3, Lean, SPIR-V) — Wasm and Wasm-GC keep the original prog because Codegen.gen_imports handles their cross-module needs natively. Smoke-tested: caller_ok.affine (use CrossCallee::{consume}) now compiles to JS / Julia / Rust / Lua with consume's body inlined. The non-JSON compile_file path also gained ~loader on its previously-loaderless Codegen.generate_module call (latent bug for cross-module wasm via this path). 188 → 190 tests; 0 regressions."
session-note-2026-05-03 = "TYPED-WASM CROSS-MODULE CLOSURE + MCP CARTRIDGE REWIRE + WASMGC LOUD-FAIL HARDENING. (1) lib/codegen.ml — generate_module gained ?loader and a new gen_imports pass that walks prog.prog_imports, loads each referenced module via Module_loader, and emits one (import \"<modpath>\" \"<fn>\" (func ...)) entry per imported function plus a (local_alias_name → import_func_idx) entry in func_indices. ImportSimple is namespace-only (no emit), ImportList emits per item, ImportGlob enumerates public TopFns. Closes the cross-module WASM import emission gap called out in session-note-2026-04-19-a — `verify-boundary CALLEE.affine CALLER.affine` now works on user-authored AffineScript pairs, not just hand-assembled bridges. (2) lib/resolve.ml — import_resolved_symbols / import_specific_items / ImportGlob inline path now also write to dest type_ctx.name_types (not just var_types), with a new lookup_source_scheme helper that falls back from sym_id-keyed source_types to name-keyed source_name_types because resolve_and_typecheck_module's per-decl Typecheck.check_decl populates name_types but never var_types. lib/typecheck.ml — Typecheck.check_program gained ?import_types : (string, scheme) Hashtbl.t that seeds name_types after register_builtins, supplied by the resolver. bin/main.ml compile_file (JSON + non-JSON paths), compile_to_wasm_module, verify_file all updated to thread import_type_ctx.name_types through and pass ~loader to Codegen.generate_module. (3) test/e2e/fixtures/ — CrossCallee.affine + cross_caller_{ok,dup,drop}.affine, plus 3 new alcotest cases under E2E Boundary Verify exercising the full pipeline (parse → resolve_with_loader → typecheck-with-import-types → codegen-with-loader → Tw_interface.verify_cross_module). All three boundary outcomes (clean / LinearImportCalledMultiple / LinearImportDroppedOnSomePath) confirmed end-to-end. (4) boj-server/cartridges/typed-wasm-mcp — mod.js rewritten to call `affinescript` (was: nonexistent `typed-wasm` binary), with cwd set to the source's directory so Module_loader resolves relative imports correctly. cartridge.json bumped to v0.2.0 with corrected input descriptions (.affine source paths) and a new typed_wasm_verify_boundary tool exposing the cross-module verifier. README.adoc updated to match. (5) lib/codegen_gc.ml — eliminated three silent-bad-codegen fallbacks (same class as BUG-005): wildcard ExprLambda/ExprUnsafe → RefNull replaced with explicit UnsupportedFeature errors; match-arm wildcard PatTuple/PatRecord → fall-to-default replaced with UnsupportedFeature; PatLit fallback for LitFloat/LitString replaced with explicit errors. test/test_e2e.ml gained E2E WasmGC Loud-Fail suite with 2 regression markers. Bumps wasm-gc-codegen from 70% to 85% (silent-fallback gap is gone; effects/try-catch/lambda/call_ref remain genuinely deferred to upstream EH proposal or whole-program CPS). 180 → 182 tests; 0 regressions."
session-note-2026-05-02 = "FACE BUILDOUT + BRAND-SURFACE EJECT + TS-EXEMPTION DOCS + FRONTIER-PRACTICES FORMALIZATION. (1) lib/lucid_face.ml + lib/cafe_face.ml — full transformer parity with python_face/js_face/pseudocode_face. lib/face_pragma.ml — pragma detection (`# face: <name>` / `// face: <name>` / `-- face: <name>` / `(* face: <name> *)`) with alias table covering canonical/python/py/rattle/rattlescript/js/javascript/jaffa/jaffascript/pseudocode/pseudo/pseudoscript/lucid/lucidscript/purescript/ps/cafe/cafescripto/coffee/coffeescript. (2) Single .affine extension across all faces; per-face extensions (.rattle/.pyaff/.jsaff) deprecated to migration path with warning. (3) lib/face.ml extended with format_*_for_face dispatch tables for Lucid (Haskell-flavoured: 'Linearity error', 'Variable not in scope', 'Could not match type') and Cafe (concise JS-flavoured). (4) tools/run_face_transformer_tests.sh + just test-faces / test-faces-record / test-faces-update — snapshot + round-trip parse harness. Caught 5 transformer bugs on first run that visual review had missed. (5) examples/faces/ + README.adoc with per-face hello programs and 'Known transformer gaps' list for the categories deferred to a future AST-rewriter milestone (multi-clause defs, do-notation, where-blocks, list comprehensions, splats, no-paren calls). (6) Five non-canonical faces ejected to top-level brand-surface repos: github.com/hyperpolymath/{rattlescript,jaffascript,pseudoscript,lucidscript,cafescripto}. Each has README, LICENSE, CONTRIBUTING, bin/<face> shim that exec's affinescript with `--face <name>` injected after the subcommand, justfile, examples/. NO compiler forks — all live and evolving compiler logic stays here in affinescript. Eject pattern: 'adapt-then-commit' for the pre-existing rattlescript repo (preserved 7 commits + v0.1.0-alpha tag while replacing the wrong-shape Cargo+vendored architecture). (7) docs/guides/frontier-programming-practices/Human_Programming_Guide.adoc + AI.a2ml — new 'Faces: Frontend Surfaces' section formalizing 6 lessons earned during the buildout: faces-as-brand-surfaces (not compiler forks), identity-in-content-not-filesystem, snapshot-plus-roundtrip for any pure text-to-text transformer, examples-are-tests-not-demonstrations, error-vocabulary-cost-O(faces × error_kinds), span-fidelity-honesty (acknowledge UX regression rather than paper over). AI.a2ml gained parallel (faces) section mirroring the existing (backends) shape with rules, when-adding-face-N+1 recipe, known-limitations. (8) .claude/CLAUDE.md TypeScript Exemptions table — 9 approved files with rationale and unblock condition. Mirror tables landed in standards / my-lang / boj-server. Audit follow-up issues #63-66 track port work blocked on #35 (Node target), #42 (extern types), and stdlib network/crypto. Commits: 74024c6 (Lucid+Cafe + unification), 82eec92 (rattlescript subtree eject), 133bb53 (frontier guide section), 116ea5d (CLAUDE.md exemptions). Side effect across the wider hyperpolymath estate: TS file count 94 → 27 after vendored-snapshot deletions (rsr-template-repo, idaptik dlc subtrees migrated to dedicated repos via PRs #1 against idaptik-dlc-iky and idaptik-dlc-reversibley + cleanup PR #70 against idaptik). Burble canary CI gate (.github/workflows/affinescript-canary.yml in PR #21) lands an advisory gate compiling every .affine file on PR — promote to required-for-merge as Burble grade B target."
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."
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."
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."
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."
session-note-2026-04-11-h = "STAGE 5 (AFFINATEA DRIVES SCENE) COMPLETE: teaDrive(engine, msgTag) is the single entry point from all 4 button handlers — update → applyView → navigate. applyView reads selected_tag via getSelected() and alpha-pulses the chosen button (0.65 → animate 1.0 over 0.22s). teaBridge loaded asynchronously on first show; graceful fallback to direct ReScript navigation when bridge is None. resize handler calls AffineTEA.setScreen(tea, width, height) to keep screen_w/h in sync with canvas. Button handlers reduced to single teaDrive call each. Navigation decision comes FROM Wasm state (selected_tag), not from button identity. Credits button wired (msgCredits) with TODO screen. IDApTIK TitleScreen is fully AffineTEA-driven for Stages 4+5."
session-note-2026-04-11-g = "STAGE 4 (WASM/JS BRIDGE FOR IDAPTIK) COMPLETE: lib/tea_bridge.ml generates complete TitleScreen TEA bridge Wasm 1.0 module (512 bytes). TitleModel at offset 64: screen_w(+0), screen_h(+4), bgm_playing(+8), selected_tag(+12). 7 functions: init, update (branchless selected_tag=msg+1), get_screen_w/h/bgm_playing/selected, set_screen. affinescript.ownership custom section: update param kind=1 (Linear). affinescript.tea_layout custom section: version=1, 4 fields. affinescript tea-bridge subcommand added to bin/main.ml. 6 bridge E2E tests added; 101 total tests passing. AffineTEA.js + AffineTEA.res bindings in idaptik/src/app/tea/. titlescreen.wasm (512 bytes, WebAssembly.validate=true, end-to-end Node.js verified) in idaptik/public/assets/wasm/."
session-note-2026-04-11-f = "LSP PHASE C COMPLETE: completion candidates subcommand shipped. extract_prefix_at scans backward from cursor col to extract ident prefix + dot-context flag. collect_completions filters symbol table by prefix and appends affine_keywords (suppressed in dot context). emit_completions emits JSON array {name,kind,type,detail}. complete_file + complete_cmd added to bin/main.ml (FILE LINE COL args). 6 E2E tests. 101 total, 0 regressions. NEXT: LSP Phase D."
session-note-2026-04-11-e = "LSP PHASE B COMPLETE (commit 79c0829): hover/goto-def subcommands shipped. span_contains, find_symbol_at (refs-first then def-spans), hover_to_json/goto_def_to_json, emit_hover/emit_goto_def, run_pipeline_for_query. 89 tests. NEXT: LSP Phase C (completion candidates from symbol table)."
session-note-2026-04-11-d = "TRAITS WIRED (commit 1ca143e): trait_registry in context; TopTrait/TopImpl in forward pass; ExprField trait fallback; find_impl unification-based; TopImpl bodies typechecked; 80 tests. NEXT: LSP Phase B."
session-note-2026-04-11-c = "LINEAR ARROWS ENFORCED (commit d2f9b7b): (1) lambda synth — |@linear x: T| e now synthesises T -[1]-> U; (2) lambda check mode — explicit quantity annotation validated against expected TArrow; (3) quantity.ml ExprLambda — annotated params declared in env, usage verified, violations accumulated in env.errors and drained after check_function_quantities step 3; scope leakage prevented via quantities save/restore. Also fixed codegen.ml missing custom_sections field (pre-existing wasm.ml change). 75 tests, 0 regressions. NEXT: traits generic resolution → unification integration; then LSP Phase B."
session-note-2026-04-11-b = "BUG-005 CLOSED: WasmGC silent drop+null fallbacks replaced with explicit CodegenError (UnboundFunction for missing func_indices entries, UnsupportedFeature for indirect/higher-order calls). Effects runtime SHIPPED: TopEffect now registers each op as a PerformEffect-raising VBuiltin in interp.ml (was no-op). Multi-arg handler arg_vals bug fixed. ExprResume wired to __resume__ continuation in handler env. WasmGC path: TopEffect ops registered as unreachable stubs with correct func_indices offsets; ExprHandle and ExprResume reject with UnsupportedFeature (EH proposal or CPS transform required for full dispatch)."
[project-context]
name = "affinescript"
completion-percentage = 94
phase = "affinatea-stage6-complete"
tagline = "Rust-inspired language with affine types, dependent types, row polymorphism, and extensible effects"
note = "Session 2026-04-11 final checkpoint. DONE THIS SESSION: (1) JS-face (lib/js_face.ml) + Pseudocode-face (lib/pseudocode_face.ml) — both wired into face.ml and bin/main.ml. resolve_face() wired into ALL six file handlers in bin/main.ml (parse/check/eval/compile/fmt/lint) — .rattle/.pyaff/.jsaff/.pseudoaff auto-detected without --face flag. (2) P3 packages: packages/affine-js/ (Deno ESM WASM loader, marshal/unmarshal, types.d.ts), packages/affine-ts/ (typed call helpers), packages/affine-res/ (ReScript bindings). (3) P4 educational: docs/guides/frontier-guide.adoc (6-chapter tutorial), docs/guides/warmup/ (4 warmup scripts). (4) CoffeeScript + ActionScript roadmap faces added to docs/specs/faces.md. (5) RattleScript distribution: distributions/rattlescript/ in-tree (Rust wrapper, build.rs, examples/), standalone repo hyperpolymath/rattlescript (GitHub, starred, v0.1.0-alpha released, 8 topics set, affinescript submodule, just bootstrap workflow). (6) dune build + dune runtest: clean, 0 regressions throughout. NEXT SESSION (clean thread): BUG-005 first (WasmGC silent bad codegen — replace fallback arms with CodegenError, ~30min), then effects runtime (interpreter handler dispatch + WASM codegen — the headline missing feature). Previous session note: P3 (aggregate library ecosystem) and P4 (educational materials) complete. P2 faces extended. P2 faces extended: JS-face (lib/js_face.ml) and Pseudocode-face (lib/pseudocode_face.ml) added, face.ml and bin/main.ml updated with all four active faces (canonical/python/js/pseudocode). aggregate packages: packages/affine-js/ (Deno ESM, WASM loader, marshal/unmarshal, types.d.ts), packages/affine-ts/ (typed call helpers, narrowing predicates), packages/affine-res/ (ReScript bindings with typed shortcuts and exhaustive matching). educational materials: docs/guides/frontier-guide.adoc (6-chapter unveiling tutorial), docs/guides/warmup/ (4 warmup scripts: basics/ownership/effects/rows). faces.md updated with JS-face and Pseudocode-face entries. Previously: Faces architecture (ADR-010, Priority 2) complete 2026-04-11. (1) lib/python_face.ml: source-level text preprocessor mapping Python surface syntax to canonical AffineScript — indentation→braces, def/True/False/None/and/or/not/class/pass keyword subs, elif/else chains, tail-position semicolon suppression. (2) lib/face.ml: face-aware error formatter mapping canonical error terms to face-specific vocabulary — Python face uses 'Ownership error / single-use variable' for linear violations, 'Name not found' for unbound vars, 'if/else type mismatch' for branch mismatches, renders Unit as None and Bool as bool. (3) bin/main.ml: --face [canonical|python] flag on all subcommands; parse_with_face selects preprocessor path; all error sites route through Face.format_*_error. (4) Span fidelity deferred: error spans currently refer to transformed canonical text positions (planned follow-up). (5) docs/specs/faces.md: face architecture reference doc written. (6) panic-attack audit: 1 High (search.js innerHTML — false positive but fixed to char-replacement), 2 Medium (LSP unwraps/shell heredoc — both false positives), 2 Low (TODO markers — informational). 73/73 E2E tests: 0 regressions. Seam check (items 1+2) verified: --face python on @linear double-use produces correct Python-face ownership error end-to-end."
[components]
lexer = "complete-minus-affine-tokens (ZERO/ONE tokens for quantity literals NOT emitted; blocks QTT surface syntax). float_exponent now correctly named sub-regexp — scientific notation crash fixed 2026-04-11."
parser = "conformance-complete (ADR-009 achieved 2026-04-11). 12/12 valid tests parse. Added: optional SEMICOLON on type aliases/enums, enum leading-pipe syntax, effect op type params, ROW_VAR type params, ty_record_body/expr_record_body recursive rules for row-polymorphic records, field_name contextual keyword rule, struct literal expressions. CORE-03 / ADR-014 (PR #241, Refs #228, 2026-05-19, gate 267/267, ZERO Menhir conflict delta — 21 S/R + 1 R/R unchanged): module-qualified type/effect paths Pkg.Type / Pkg::Type (mixed, deep) parse — folded to one canonical ::-joined ident so resolve/typecheck/codegen/fmt unchanged. Cleared the estate's dominant parse blocker (was 525/1177). Authoritative: docs/CAPABILITY-MATRIX.adoc + docs/TECH-DEBT.adoc CORE-03."
type-checker-rules = "99% (bidirectional inference, Never/bottom, block divergence, variant/constructor/record-spread/mutable bindings all implemented in lib/typecheck.ml)"
type-checker-enforcement-wiring = "wired (Typecheck.check_program is invoked from bin/main.ml on every check/compile/eval path; Quantity.check_program_quantities runs after typecheck at lib/typecheck.ml:1206; the gating IS live for the rules and quantity annotations that exist today)"
borrow-checker = "live-gate (2026-04-11): Borrow.check_program wired into check/compile pipeline at all 4 Typecheck success sites. Emits E0501-E0506 diagnostics. PlaceVar carries variable name for readable errors (file:line:col at both use and move sites). ExprMatch arm state merging fixed. Lexical borrow lifetime clearing in check_block. BUG-004 (lambda capture tracking) still deferred — requires type info propagation. lib/borrow.ml 669 LOC."
interpreter = "95%"
wasm-codegen = "95% (cross-module imports + extern fn → WASM import emission for both `use Foo::{f}` and `extern fn f` user-source forms; landed 2026-05-03c)"
wasm-gc-codegen = "91% (variant-with-args + PatCon same-arity destructuring 2026-05-03c. Silent-bad-codegen fallbacks eliminated 2026-05-03a. Mixed-arity matches still need uniform variant rep; effects/try-catch/call_ref still deferred to upstream EH or CPS.)"
node-cjs-codegen = "Phase 1+2+3 complete (2026-05-03d): codegen_node.ml emits CJS shim; stdlib/Vscode.affine + stdlib/VscodeLanguageClient.affine ship 19 binding declarations; packages/affine-vscode/mod.js is the JS-side adapter (now using shared handle table via exposed exports._instance / exports._registerHandle); editors/vscode/src/extension.ts deleted, replaced by extension.affine compiled to out/extension.cjs (verified end-to-end: real Node 18 dispatches activate(ctx), 5 commands register, lsp.enabled probed). package.json's main field now ./out/extension.cjs; compile script invokes `affinescript compile`. tools/check-no-extension-ts.sh + .github/workflows/ci.yml step + just check guard ensure the .ts cannot drift back. CLAUDE.md exemptions table reduced from 9 → 8 with the closed-exemption note. Phase 4 (rattlescript-face sweep) is the only remaining task on this issue."
session-note-2026-05-03-e = "STDLIB-PARSER UNBLOCKERS: 3 type-syntax sugars added to lib/parser.mly. (1) `fn(A, B, ...) -> T` lowers to the curried arrow chain `A -> B -> ... -> T`. Zero-arg `fn() -> T` lowers to `Unit -> T` (TyTuple [] -> T). Required by stdlib/Option.affine's `unwrap_or_else[T](opt: Option[T], f: fn() -> T) -> T` and similar higher-order signatures. (2) Angle-bracket aliases for type application AND type parameters: `Option<T>` ≡ `Option[T]`, `fn f<T>(x: T)` ≡ `fn f[T](x: T)`, `type Option<T> = ...` ≡ `type Option[T] = ...`. The `<` / `>` are unambiguous in type position because comparison operators don't appear there. (3) `(A, B) -> C` now lowers to the curried arrow `A -> B -> C` instead of being parsed as a single tuple-arg arrow. Disambiguated from plain tuple types by the trailing ARROW; bare `(A, B)` (no arrow) still parses as a TyTuple. Stdlib/Core.affine's `flip` reverted from the earlier curried workaround to its natural `(A, B) -> C` form. Stdlib audit: 4 files now check (Core, Math, Vscode, VscodeLanguageClient), 5 parse but fail check (Option/Result/io/prelude/string — actual code-level errors), 7 still fail to parse (collections/effects/option/result/math/testing/traits) with DISTINCT remaining issues — slice syntax `list[1:]`, `effect io;` decl form, `=>` lambda spelling, `Self` in trait method sigs. Each is its own task. 7 new regression tests under E2E Type Syntax Sugar. 200 → 207 tests; 0 regressions."
session-note-2026-05-03-d = "ISSUE #35 PHASE 3 CLOSED + REGRESSION GUARD WIRED. (1) editors/vscode/src/extension.affine (160 lines) replaces extension.ts, using the Phase 2 Vscode + VscodeLanguageClient bindings from stdlib/. Adds 9 new extern fns to Vscode.affine for the API surface the migration actually needed: editorActiveFilePath / editorActiveLanguageId (collapse vscode.window.activeTextEditor.document.{uri.fsPath,languageId}), workspaceConfigGetBool, consoleLog, execSync, stringConcat / stringEndsWith / stringReplaceSuffix. (2) packages/affine-vscode/mod.js refactored to take the host shim as 3rd arg and share its handle table — fixes a bug where the wasm-side ExtensionContext handle wasn't visible to adapter functions. lib/codegen_node.ml's shim now exposes exports._instance + exports._memory after init so adapters can read string args out of wasm linear memory; replaces the constant `_extraImports()` with a mutable exports.extraImports hook. (3) editors/vscode/package.json `main` field flipped from ./out/extension.js to ./out/extension.cjs; scripts.compile is now `affinescript compile src/extension.affine -o out/extension.cjs`; obsolete tsconfig.json deleted; @types/node + typescript devDependencies removed. (4) tools/check-no-extension-ts.sh fails CI / `just check` if any extension.ts reappears under editors/vscode/src or faces/*/affinescript/editors/vscode/src — wired into both .github/workflows/ci.yml and the just check recipe. (5) .claude/CLAUDE.md TypeScript Exemptions table: editors/vscode/src/extension.ts row removed (count 9 → 8), moved to a 'Closed exemptions' note with the 2026-05-03 closure date and pointer to the regression guard. End-to-end smoke-tested: real Node 18 require()s the .cjs, dispatches activate(fakeContext), all 5 commands register with their full names, both lsp.enabled and lsp.serverPath are probed via getConfiguration. 200 tests still green; 0 regressions."
extern-decls = "complete (2026-05-03c): `extern fn` and `extern type` both parse, resolve, typecheck, and emit (import \"env\" \"<name>\" ...) in the WASM target."
array-type-sugar = "complete (2026-05-03c, closes issues-drafts/02): `[T]` desugars to Array[T] in any type-expr position."
type-syntax-sugars = "complete (2026-05-03e): three forms accepted — `fn(A,B) -> T` (curried lowering), `Option<T>` / `Result<T,E>` / `fn f<T>` (angle-bracket aliases for both type-app and type-param), and `(A,B) -> C` (true 2-arg arrow distinct from tuple-arg). Plain tuple types still parse when no ARROW follows."
xmod-other-codegens = "complete (2026-05-03b): Module_loader.flatten_imports inlines public TopFns from imported modules; threaded through all 22 non-Wasm backends in bin/main.ml. Smoke-tested with JS/Julia/Rust/Lua emission of caller-of-Callee."
julia-codegen = "exists"
lsp-phase-a = "complete"
lsp-phase-b = "complete (2026-04-11, commit 79c0829): hover/goto-def subcommands shipped. json_output.ml: span_contains (1-based, single+multi-line), find_symbol_at (references-first then def-spans), hover_to_json/goto_def_to_json/not_found_json, emit_hover/emit_goto_def. bin/main.ml: hover + goto-def subcommands, run_pipeline_for_query tolerates typecheck errors. 4 E2E tests. 89 total."
lsp-phase-c = "complete (2026-04-11): completion candidates subcommand shipped. json_output.ml: extract_prefix_at (scans backward from cursor col to find ident prefix + dot-context flag), collect_completions (symbol table prefix filter + affine_keywords list suppressed in dot context), emit_completions (JSON array). bin/main.ml: complete_file handler + complete_cmd (FILE LINE COL args). 6 E2E tests added (prefix extraction, prefix match, empty prefix, no-match, keyword included, dot-ctx suppresses keywords). 101 total tests, 0 regressions."
lsp-phase-d = "complete (2026-04-11): JSON-RPC LSP server shipped as `affinescript server --stdio`. lib/lsp_server.ml: Content-Length framing, document store (URI→source), pipeline cache (URI→symbols+refs+diags), run_pipeline (temp-file + span path fixup), lsp_range (1→0-based conversion), diag_to_lsp, publish_diagnostics. Handlers: initialize (hover+definition+completion caps), didOpen/didChange (pipeline+cache+push), didClose (clear), hover (markdown+type+quantity), definition (URI+range), completion (LSP kind mapping). bin/main.ml: server_cmd in default group. 4 E2E tests. 105 total, 0 regressions."
stdlib = "95% (5 stubs remain as extern builtins)"
[stats]
compiler-loc = 12750
compiler-modules = 39
lsp-files = 5
test-files = 54
[features]
# Honest status of the headline language features advertised in README.adoc.
# "declared-but-unwired" = surface syntax and/or internal module exists, but
# the feature is not enforced on user programs through the CLI pipeline.
affine-types = "wired-and-reachable (Track A Manhattan plan complete 2026-04-10. Quantity semiring in lib/quantity.ml; invoked from typecheck.ml:1206 inside the standard CLI pipeline. Surface syntax per ADR-007 hybrid: @linear/@erased/@unrestricted attributes (Option C primary) on let/stmt-let/param/lambda-param, AND :1/:0/:ω numeric sugar (Option B) on let/stmt-let. Scaled Let rule per ADR-002 implemented in lib/quantity.ml ExprLet/StmtLet — closes BUG-001 (ω-let smuggles linear values) and BUG-002 (zero-let erasure). Four regression fixtures in test/e2e/fixtures/ exercise both surface forms. Behavioural enforcement verified via E2E Quantity test suite — 4 new passing tests, 0 regressions.)"
linear-arrows = "enforced (2026-04-11): Three-part fix landed. (1) typecheck.ml lambda synth: |@linear x: T| e now synthesises T -[1]-> U (was always QOmega). (2) typecheck.ml lambda check mode: explicit param quantity annotation validated against expected TArrow quantity; unannotated params inherit context quantity. (3) quantity.ml ExprLambda: added env.errors accumulator; annotated lambda params declared via env_declare so env_use tracks them; usage verified with check_quantity after body walk; violations pushed to env.errors and drained at end of check_function_quantities (step 4). Saved/restored env.quantities entries to prevent scope leakage. Two E2E fixtures + 2 passing tests. 75 tests total, 0 regressions. Commit d2f9b7b pushed."
borrow-checker = "phase-3-parts-1-3-Slices-A-B-C-light-landed (CORE-01, Refs #177, 2026-05-24): pt1 (#240, gate 263/263) borrow-graph validation wired — BorrowOutlivesOwner emitted, shared-XOR-exclusive enforced at use sites, ownership from param type TyOwn/TyRef/TyMut, call-arg borrows temporary, ref-binding graph tracked. pt2 (gate 271→274 and 278→281) return-escape + &mut e parser surface. pt3 Slice A (PR #335) NLL last-use expiry: compute_last_use_index pre-pass; check_block expires in-block ref-bindings once their binder is dead. pt3 Slice B (effectively #354/#355/#356 after #351 procedural close) flow-sensitive escape via re-assignment: `outer = &y` in StmtAssign pre-releases the held borrow and re-binds the ref-graph entry to the freshly-created borrow. pt3 Slice C-light (this PR) CFG-join semantics for non-match join constructs: ExprHandle handler arms and ExprTry catch arms are now isolated via snapshot-restore-merge (factored merge_arm_results helper, mirroring ExprMatch's inlined logic) — moves/borrows from arm i no longer pollute arm i+1. 2 hermetic tests (positive: independent catch-arm moves OK; anti-regression: body-side move persists past the try). Residual (Slices C-full, C', D): true Polonius origin/region variables on TyRef/TyMut with subset constraints + datalog-style solver (architectural; ADR-gated); loop soundness via 2-iter check (coupled to StmtAssign clear-on-rewrite fix); reborrow through indirection (ref-to-ref binding); quantity-checker tightening for captured linears. Authoritative: docs/CAPABILITY-MATRIX.adoc + docs/TECH-DEBT-alt.adoc CORE-01."
row-polymorphism = "60% (records + effects rows implemented in typecheck/unify; not fully exercised end-to-end)"
effects = "interpreter-complete (handler dispatch, PerformEffect propagation, ExprResume, multi-arg ops all wired in interp.ml 2026-04-11). WasmGC: ops registered as unreachable stubs; ExprHandle/ExprResume reject with UnsupportedFeature — full WASM dispatch needs EH proposal or CPS transform."
dependent-types = "parse-only (TRefined AST node exists and refinement predicates parse, but predicates do not reduce; no SMT/decision procedure wired in)"
traits = "90% (2026-04-11, commit 1ca143e): trait_registry added to typecheck context; TopTrait/TopImpl processed in forward pass; ExprField falls back to Trait.find_method_for_type on record-field failure; find_impl/find_impls_for_type use unification-based matching via fresh_impl_self_ty + Unify.unify instead of name string comparison; TopImpl bodies type-checked via check_fn_decl with Self bound; check_impl_satisfies_trait verifies required methods present. Two E2E fixtures + 2 tests. 80 total, 0 regressions. Remaining: associated type substitution in method bodies, where-clause supertrait enforcement, coherence checking.)"
[track-a-manhattan]
owner = "primary"
scope = """
Close the surface-syntax gap that makes affine enforcement unreachable from
user programs. Revised 2026-04-10 after the original ETA was set against the
incorrect premise that `Quantity.check_program_quantities` was unwired from
the CLI — it is wired (typecheck.ml:1206) and has been since at least
the Manhattan-recovery refactor (#19). The actual remaining work:
1. AST: add `el_quantity : quantity option` to ExprLet, `sl_quantity` to
StmtLet. (~4 LOC + cascading pattern updates across ~10 modules that
match on Let.)
2. Lexer/parser: make ZERO/ONE tokens reachable in quantity position
(cheapest path: accept INT 0/INT 1 in the parser's `quantity` rule,
leaving the lexer alone). ~3 LOC.
3. Parser: extend `let_decl` and `stmt_let` rules with an optional
binder-quantity annotation. Surface-syntax choice is a language-design
call — see META.a2ml ADR-007 candidate.
4. quantity.ml ExprLet/StmtLet: implement context scaling per ADR-002.
Snapshot before walking el_value, walk it, scale all newly-recorded
usages by `q`, then merge back. ~25 LOC including a `scale_usage_by`
helper.
5. Two regression fixtures covering BUG-001 and the dual valid case.
6. spec.md T-Let rewrite to show the scaling explicitly.
Out of scope for Track A:
- BUG-003 (eval_list L-to-R): SHIPPED 2026-04-10. interp.ml:347 now uses
fold_left + List.rev. Behavioural regression test deferred — depends on
the broken `let mut` interpreter path (separately tracked in the failing
baseline interp tests).
- BUG-004 (lambda capture tracking): blocked on borrow checker Phase 3.
- BUG-002 (zero-let erasure semantics): folds in with BUG-001 once
surface syntax exists.
"""
spine-files = ["lib/ast.ml", "lib/parser.mly", "lib/quantity.ml", "lib/typecheck.ml", "lib/interp.ml", "lib/codegen.ml", "lib/codegen_gc.ml", "lib/julia_codegen.ml", "lib/formatter.ml", "lib/sexpr_dump.ml", "lib/json_output.ml", "lib/linter.ml", "lib/opt.ml", "lib/desugar_traits.ml", "test/e2e/fixtures/", "docs/spec.md"]
eta = "T+7d from 2026-04-10 (ADR-007 accepted, implementation chain unblocked)"
blocking-decision = "RESOLVED 2026-04-10: ADR-007 accepted as hybrid C-primary + B-sugar. @linear/@erased/@unrestricted are the canonical forms emitted by tutorials, error messages, IDE tooling, and the formatter. The :1/:0/:ω numeric forms are accepted as sugar (legal in source, rewritten to attribute form by `affinescript fmt` unless --keep-quantity-sugar is set). See META.a2ml ADR-007 for full decision text."
[[closed-bug]]
id = "BUG-001"
severity = "high"
category = "soundness"
title = "ω-let smuggles linear values"
discovered = "2026-04-10"
fixed = "2026-04-10"
description = """
let x :ω = linear_resource() in use_x_once(x) was previously accepted.
The QTT-orthodox scaled Let rule (ADR-002) rejects this because scaling
the value context by ω promotes the linear usage of `linear_resource()`
to UMany, which the quantity checker reports as a linear-binding violation.
"""
fix = "lib/quantity.ml ExprLet/StmtLet cases now snapshot the env, walk the value, scale the per-variable delta by the binder quantity (via the new scale_usage helper), and merge the scaled deltas back. ExprLet was extended with el_quantity, StmtLet with sl_quantity (lib/ast.ml). Surface syntax landed per ADR-007 hybrid: @linear/@erased/@unrestricted primary + :1/:0/:ω sugar."
verification = "test/e2e/fixtures/bug_001_omega_let_smuggles_linear.affine (Option C form) and test/e2e/fixtures/bug_001_sugar_form.affine (Option B form) — both produce the @linear-vocabulary error message and are pinned in test/test_e2e.ml as `BUG-001 attr/sugar form rejects ω-let smuggling @linear`. Behaviourally verified end-to-end through `dune runtest`. The dual valid cases (affine_let_valid.affine and affine_let_valid_sugar.affine) confirm the rule does not over-reject."
[[closed-bug]]
id = "BUG-002"
severity = "medium"
category = "semantics"
title = ":0 lets do not erase their RHS"
discovered = "2026-04-10"
fixed = "2026-04-10"
description = """
let x :0 = expensive_proof_term() in body_not_using_x previously evaluated
expensive_proof_term() and consumed its resources. The QTT-orthodox scaled
Let rule scales the value context by 0, producing the zero context, which
means the RHS carries no runtime obligations and may be erased.
"""
fix = "Closed via the same scaled-Let infrastructure as BUG-001. The quantity-checking pass now correctly drops usage contributions from the RHS of an @erased-bound let (scale_usage QZero _ = UZero), so any linear variables consumed in the RHS no longer count against their owner. The optional interpreter-side erasure (skipping eval of @erased-bound RHS) is deferred — it is an optimisation, not a correctness requirement."
verification = "Covered transitively by the BUG-001 fixtures: the same scaling logic that closes BUG-001 closes BUG-002. A focused @erased fixture is on the follow-up backlog but not required to declare the bug fixed at the rule level."
[[closed-bug]]
id = "BUG-003"
severity = "medium"
category = "semantics"
title = "eval_list evaluates right-to-left via List.fold_right"
discovered = "2026-04-10"
fixed = "2026-04-10"
description = """
lib/interp.ml:347 eval_list previously used List.fold_right with monadic
bind, which in strict OCaml evaluates arguments right-to-left. ExprBinary
was already left-to-right, creating internal evaluator inconsistency and a
latent divergence point for future effects/affine enforcement. See ADR-003.
"""
fix = "Replaced fold_right with an explicit L-to-R recursive loop that accumulates results in reverse and reverses at the end. See lib/interp.ml `eval_list` (~12 LOC including the doc comment referencing ADR-003)."
regression-test-status = "deferred — observable order verification requires either the mutable-binding interpreter path (currently broken in baseline; the failing 'lambda'/'simple eval' interp tests share the same root cause) or a side-effecting builtin reachable from a fixture. Both routes are out of scope for the BUG-003 fix and should be revisited once the interpreter mutation path is repaired."
verification = "Code review against ADR-003 + dune build clean + dune runtest baseline-equivalent (22 pre-existing failures, none new, none resolved)."
[[closed-bug]]
id = "BUG-004"
severity = "medium"
category = "soundness"
title = "Lambda-body usage not tracked against outer captures"
discovered = "2026-04-10"
fixed = "2026-04-11"
description = """
lib/quantity.ml ExprLambda was a no-op for outer variable usage: captures
were not scaled by QOmega, so a lambda capturing a linear variable did not
raise LinearVariableUsedMultiple. Borrow checker also ignored captures,
allowing move of a captured variable after lambda creation.
"""
fix = """
Two coordinated fixes (commit 48422d1):
1. quantity.ml ExprLambda: snapshot env, walk body (shadowing lambda params),
compute per-variable delta, restore env, re-apply delta scaled by QOmega.
scale_usage QOmega UOne = UMany → LinearVariableUsedMultiple for linear captures.
2. borrow.ml ExprLambda: collect_free walker finds all free variables in body,
creates Shared borrows for each captured place. Move of captured variable
→ MoveWhileBorrowed. Borrows expire at block exit via lexical-lifetime clearing.
borrow_kind_name replaces show_borrow_kind for readable error messages.
"""
verification = "Manual smoke tests: lambda capturing moved value → 'cannot move `v` while shared-borrowed'; valid lambda (no owned captures) → passes. 73/73 tests pass."
[[closed-bug]]
id = "BUG-005"
severity = "medium"
category = "codegen"
title = "WasmGC backend silently drops unknown function calls"
discovered = "2026-04-10"
fixed = "2026-04-11"
description = """
lib/codegen_gc.ml ExprApp handling silently emitted 'drop all args + push null'
when the callee was not in the func_indices table, and did the same for
indirect calls. This was placeholder behaviour that produced wrong code rather
than failing loudly.
"""
fix = "Both fallback arms replaced with explicit CodegenError. Direct-call miss → Error (UnboundFunction id.name). Indirect/higher-order callee → Error (UnsupportedFeature \"indirect / higher-order call in WasmGC backend (call_ref not yet implemented)\"). New UnboundFunction variant added to codegen_error and format_codegen_error. commit b58178a."
regression-test-status = "landed 2026-05-23 — test/test_e2e.ml `test_gc_unbound_function_loud_fail` in `wasm_gc_loud_fail_tests`. Parses `fn main() -> Int { return totally_undefined_callee(42); }`, feeds it directly to Codegen_gc.generate_gc_module (no resolve step), asserts the emitted error message contains 'totally_undefined_callee' — which UnboundFunction's format string includes verbatim. Pins the explicit-error path so a regression to silent drop+null would fail the gate."
# ─── Deferred upgrade: bring .machine_readable/ to current standards ──────────
# AffineScript predates most of the current hyperpolymath .machine_readable/
# suite. The 6a2 core is present; the surrounding machinery is not. This is
# logged as deferred work, separate from the Manhattan recovery, to avoid
# scope creep into that plan.
[deferred-upgrade]
# DONE 2026-04-12 — suite upgrade completed in dedicated session (no .ml changes)
status = "done"
completed = "2026-04-12"
added = [
".machine_readable/CLADE.a2ml",
".machine_readable/contractiles/ (migrated from root via git mv — history preserved)",
".machine_readable/contractiles/adjust/Adjustfile.a2ml",
".machine_readable/contractiles/intend/Intentfile.a2ml",
".machine_readable/agent_instructions/README.adoc",
".machine_readable/agent_instructions/coverage.a2ml",
".machine_readable/agent_instructions/debt.a2ml",
".machine_readable/agent_instructions/methodology.a2ml",
".machine_readable/integrations/verisimdb.a2ml",
".machine_readable/integrations/vexometer.a2ml",
".machine_readable/ADJUST.contractile",
".machine_readable/INTENT.contractile",
".machine_readable/MUST.contractile",
".machine_readable/TRUST.contractile"
]