-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSTATE.a2ml
More file actions
229 lines (213 loc) · 47.1 KB
/
Copy pathSTATE.a2ml
File metadata and controls
229 lines (213 loc) · 47.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
# SPDX-License-Identifier: MPL-2.0
# STATE.a2ml — Project state checkpoint
[metadata]
project = "affinescript"
version = "0.1.0"
last-updated = "2026-05-03"
status = "active"
authoritative-status-doc = "docs/CAPABILITY-MATRIX.adoc"
drift-flag = "STALE as of 2026-05-19: this file's [components]/[features]/[project-context] predate origin/main@dc5b817. It MIRRORS, it does not LEAD. For live readiness use docs/CAPABILITY-MATRIX.adoc; for the spine/contract/ledger use docs/ECOSYSTEM.adoc + docs/TECH-DEBT.adoc (DOC-05, issue #176). Gate baseline now 260/260, not 257."
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-part-1-landed (CORE-01, PR #240 Refs #177, 2026-05-19, gate 263/263): borrow-graph validation wired — BorrowOutlivesOwner emitted (&local escaping its block), shared-XOR-exclusive enforced at use sites (UseWhileExclusivelyBorrowed), ownership derived from param type TyOwn/TyRef/TyMut (owned/ref/mut discipline now enforced from real parsed source — closed a latent hole), call-arg borrows temporary, ref-binding graph tracked. Part 2+ deferred: NLL/region inference, flow-sensitive escape via `outer = &x`, tighter quantity integration. Authoritative: docs/CAPABILITY-MATRIX.adoc + docs/TECH-DEBT.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 = "deferred — fixture needs a known-unknown function name in a WasmGC compile path"
# ─── 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"
]