Skip to content

Commit fb7e855

Browse files
Roadmap tracking + Zig-FFI doc (#19) + hex/base64 encoding (#25) + Int-division codegen fix (#478) (#474)
Rebased onto `origin/main` (was 15 commits behind; clean replay, no conflicts). Built + tested locally with an apt-bootstrapped OCaml toolchain. ## 1 — Roadmap Tracking-section fixes (docs) Replaced stale `(TBD — opened alongside this PR)` placeholders in the three satellite roadmap docs (landed in #410) with the live umbrellas + child issues: | Doc | Umbrella | Child model | Filed | |-----|----------|-------------|-------| | `bindings-roadmap.adoc` | #446 | pre-filed per-tier | #450#454; kickoff #455 | | `stdlib-roadmap.adoc` | #412 | lazy (`stdlib #N`) | #415 | | `alib-roadmap.adoc` | #413 | lazy (`alib #N`) | #416 | ## 2 — Zig C-ABI FFI patterns doc (bindings #19) `docs/specs/zig-ffi-patterns.adoc` — authoring recipe, DOC-DEDUP-scoped against SPEC §2.10 / STDLIB-EXTERN-AUDIT / codegen-environment. Row `◐ → ●`. ## 3 — Hex + Base64 encoding (stdlib #25) `stdlib/encoding.affine`: hex (`to_hex`/`from_hex`/`to_hex_padded`/`hex_digit`/`hex_value`) + RFC 4648 base64 (`to_base64`/`to_base64_url`/`from_base64`/`from_base64_url`), byte-oriented via two new compiler primitives `string_char_code_at`/`string_from_char_code` (resolve/typecheck/interp/codegen_deno). Verified by `tests/codegen-deno/encoding_smoke`. Row `○ → ◑`. ## 4 — Integer-division codegen fix (#478) Type-erased Deno-ESM lowered `Int / Int` to JS float division (`255/16 → 15.9375`), breaking `math.affine`'s `pow`/`sum_naturals`/`binomial`/`lcm`/`div_floor` and `for x in xs { x/k }` over int arrays. Now a provably-`Int` `a/b` lowers to `Math.trunc(a/b)`; everything else stays `/` (no float regression). Conservative classifier covers literals, Int params, let/assign-tracked locals, integer-closed arithmetic, JS bitwise, Int-returning calls, **for-loop variables over `Array<Int>`, and `xs[i]` element reads**. Regression: `tests/codegen-deno/int_div`. ## Local verification (rebased state) | build-job step | result | |---|---| | `dune build bin/main.exe` | ✅ | | `dune runtest` | ✅ | | codegen WASM | ✅ | | codegen Deno-ESM (incl. int_div, encoding) | ✅ | | face-transformer | ✅ | | check-no-extension-ts | ✅ | | check-doc-truthing (DOC-01..09) | ✅ | | `dune build @fmt` (formatting) | verifying (ocamlformat 0.26.2 install blocked by proxy on non-github dep hosts; confirming OCaml edits are clean before merge) | Full `dune build`/`@doc` additionally need `js_of_ocaml`+`odoc` (LSP/doc paths), present in CI. Refs #446, #412, #413, #478. https://claude.ai/code/session_01DbDFzJZue7mMnKudvXSK4T
2 parents 83d82d3 + 8b40c7e commit fb7e855

17 files changed

Lines changed: 1268 additions & 35 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,7 @@ packages/affine-js/deno.lock
107107

108108
# ADR-015 S3: fetch-pinned WASI adapter (provisioned, not committed)
109109
tools/vendor/
110+
111+
# Claude Code agent worktrees — transient per-agent git worktrees,
112+
# never committed (committing a nested worktree would corrupt the repo).
113+
/.claude/worktrees/

.machine_readable/6a2/STATE.a2ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,20 @@ added = [
238238
".machine_readable/MUST.contractile",
239239
".machine_readable/TRUST.contractile"
240240
]
241+
242+
[[session-delta]]
243+
# Appended 2026-05-31 — bindings/stdlib roadmap closeout session (PR #474).
244+
# Mirror entry only; authoritative status remains docs/CAPABILITY-MATRIX.adoc
245+
# + the per-row status in docs/{bindings,stdlib}-roadmap.adoc. Live test
246+
# counts come from `dune runtest --force`, never hard-coded here (DOC-05, #176).
247+
date = "2026-05-31"
248+
pr = "#474"
249+
narrative = "docs/history/BINDINGS-STDLIB-CODEGEN-2026-05-31.{adoc,a2ml}"
250+
landed = [
251+
"bindings #19 (Zig C-ABI FFI patterns) — docs/specs/zig-ffi-patterns.adoc; roadmap row ◐->●",
252+
"stdlib #25 (hex + RFC4648 base64) — stdlib/encoding.affine; roadmap row ○->◑ (Bytes overloads deferred to #30)",
253+
"compiler: string_char_code_at / string_from_char_code byte builtins (resolve/typecheck/interp/codegen_deno)",
254+
"codegen fix #478 — Int/Int truncates via Math.trunc on Deno-ESM (Float/Float untouched); js/node/wasm follow-ups tracked in #478",
255+
"docs: bindings/stdlib/alib roadmap Tracking sections de-staled to live umbrellas #446/#412/#413"
256+
]
257+
umbrella-446 = "stays open — STEP 1-3 done, STEP 4/5 long-running"

docs/alib-roadmap.adoc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,17 @@ extensions that aLib doesn't currently capture. These items propose
304304

305305
== Tracking
306306

307-
* Umbrella tracker: (TBD — opened alongside this PR)
308-
* Per-tier child issues: (TBD — opened alongside this PR)
307+
* Umbrella tracker:
308+
link:https://github.com/hyperpolymath/affinescript/issues/413[#413]
309+
(Umbrella: aLib conformance and contributions roadmap — 25 items /
310+
3 tracks).
311+
* Per-track child issues: opened lazily as work on an item starts.
312+
Reference items by their stable doc number as `alib #N` in
313+
cross-issue discussion. Filed so far:
314+
** link:https://github.com/hyperpolymath/affinescript/issues/416[#416]
315+
— alib #10 (`stdlib/alib.affine` conformance module); closed.
309316
* Upstream tracking: `hyperpolymath/aggregate-library` (T3 items
310-
result in PRs there, not in this repo)
317+
result in PRs there, not in this repo).
311318

312319
When an item's status changes, update its row in this file in the
313320
*same* PR that lands the change; do not let the table drift.

docs/bindings-roadmap.adoc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ Surface that other estate repos are actively wanting.
166166

167167
|19
168168
|*Zig FFI canonical patterns* — `extern fn` shape for calling Zig C-ABI exports from AffineScript
169-
|`◐` idiomatic patterns exist
170-
|`stdlib` + ADR-style doc
171-
|"Zig = APIs + FFIs" estate directive. Each `hpm-*-rsr` lib exposes Zig exports; AffineScript needs canonical patterns for binding them.
169+
|`●` usable
170+
|`docs/specs/zig-ffi-patterns.adoc`
171+
|"Zig = APIs + FFIs" estate directive. Each `hpm-*-rsr` lib exposes Zig exports; AffineScript needs canonical patterns for binding them. Canonical authoring recipe + per-backend host contract (wasm `env` import vs Deno-ESM same-named host symbol) landed in `docs/specs/zig-ffi-patterns.adoc` — companion to STDLIB-EXTERN-AUDIT + SPEC §2.10; unblocks the RSR rewires #11 / #12 / #16.
172172

173173
|20
174174
|*Telegram (Grammy)* — already `stdlib/Grammy.affine`
@@ -434,8 +434,24 @@ Build, test, and cross-language surface.
434434

435435
== Tracking
436436

437-
* Umbrella tracker: (TBD — opened alongside this PR)
438-
* Per-tier child issues: (TBD — opened alongside this PR)
437+
* Umbrella tracker:
438+
link:https://github.com/hyperpolymath/affinescript/issues/446[#446]
439+
(campaign UMBRELLA).
440+
* Per-tier child issues (STEP 2 — all filed):
441+
** Tier 1 (required for current idaptik migration):
442+
link:https://github.com/hyperpolymath/affinescript/issues/450[#450]
443+
** Tier 2 (estate-wide near-term needs):
444+
link:https://github.com/hyperpolymath/affinescript/issues/451[#451]
445+
** Tier 3 (broadly useful — web universals):
446+
link:https://github.com/hyperpolymath/affinescript/issues/452[#452]
447+
** Tier 4 (backend / cloud / data):
448+
link:https://github.com/hyperpolymath/affinescript/issues/453[#453]
449+
** Tier 5 (tooling / FFI / language interop):
450+
link:https://github.com/hyperpolymath/affinescript/issues/454[#454]
451+
* Shipped work items:
452+
** Tier 1 #5 (WASM-exports calling pattern, the recommended kickoff):
453+
link:https://github.com/hyperpolymath/affinescript/issues/455[#455]
454+
(closed; landed via PR #467).
439455

440456
When a binding's status changes, update its row in this file in the
441457
*same* PR that lands the change; do not let the table drift.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Machine-readable session summary: bindings/stdlib roadmap closeout +
3+
# hex/base64 encoding + Int-division codegen fix. Bot-primary doc per
4+
# A2ML standard; see the .adoc companion for human narrative.
5+
6+
[metadata]
7+
session-date = "2026-05-31"
8+
triggered-by = "issue #446 (AffineScript top-50 framework bindings roadmap UMBRELLA) — 'drive this to conclusion'"
9+
scope = "single-session"
10+
pr = "affinescript#474"
11+
merge-method = "merge-commit"
12+
author = "Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>"
13+
14+
[summary]
15+
headline = "Roadmap-doc tracking fixes (×3) + Zig-FFI patterns spec (bindings #19) + hex/base64 stdlib module (stdlib #25) + byte primitives + Int-division codegen fix (#478, filed + fixed this session)"
16+
state-before = "bindings-roadmap/stdlib-roadmap/alib-roadmap Tracking sections carried stale '(TBD — opened alongside this PR)' placeholders; no Zig-FFI authoring doc (bindings #19 = scaffold); no stdlib/encoding.affine; Deno-ESM backend lowered Int/Int to JS float division (255/16 -> 15.9375), silently breaking math.affine integer fns"
17+
state-after = "all three Tracking sections point at live umbrellas (#446/#412/#413) + child issues; docs/specs/zig-ffi-patterns.adoc landed (row #19 ◐->●); stdlib/encoding.affine landed with hex + RFC4648 base64 (row #25 ○->◑); two byte builtins wired through resolve/typecheck/interp/codegen_deno; Int/Int division truncates via Math.trunc on Deno-ESM, Float/Float untouched"
18+
19+
[changes.docs.tracking-fixes]
20+
files = "docs/bindings-roadmap.adoc, docs/stdlib-roadmap.adoc, docs/alib-roadmap.adoc"
21+
kind = "doc"
22+
detail = "Replaced '(TBD — opened alongside this PR)' Tracking placeholders with live umbrella + child-issue links. bindings -> #446 (pre-filed tiers #450-#454, kickoff #455); stdlib -> #412 (lazy stdlib #N; #415); alib -> #413 (lazy alib #N; #416)."
23+
24+
[changes.docs.zig-ffi]
25+
file = "docs/specs/zig-ffi-patterns.adoc"
26+
kind = "doc-new"
27+
roadmap = "bindings #19"
28+
detail = "Canonical authoring recipe for binding a Zig C-ABI export to an extern fn. DOC-DEDUP-scoped vs SPEC §2.10 (grammar) / STDLIB-EXTERN-AUDIT (inventory) / codegen-environment (wasm codegen). Per-backend host contract: wasm (import \"env\" \"<name>\"); Deno-ESM same-named host symbol. Worked example uses the existing hpm-json-rsr binding."
29+
roadmap-row = "bindings-roadmap.adoc #19 ◐ -> ●"
30+
31+
[changes.stdlib.encoding]
32+
file = "stdlib/encoding.affine"
33+
kind = "stdlib-new"
34+
roadmap = "stdlib #25"
35+
surface = "to_hex, from_hex, to_hex_padded, hex_digit, hex_value, to_base64, to_base64_url, from_base64, from_base64_url"
36+
detail = "Lowercase 32-bit hex + RFC 4648 base64 (standard + URL-safe). Built on string primitives (len/string_sub/string_find/++) and integer bit-ops (& >> << |). Integer / deliberately avoided — it lowered to float division on Deno-ESM (see #478)."
37+
deferred = "Base64 Bytes-typed overloads await a Bytes type (stdlib #30); current forms operate on Latin-1-byte Strings."
38+
roadmap-row = "stdlib-roadmap.adoc #25 ○ -> ◑"
39+
test = "tests/codegen-deno/encoding_smoke.{affine,harness.mjs} — RFC 4648 §10 vectors round-trip under Node"
40+
41+
[changes.compiler.byte-primitives]
42+
files = "lib/resolve.ml, lib/typecheck.ml, lib/interp.ml, lib/codegen_deno.ml"
43+
kind = "compiler"
44+
added = "string_char_code_at (String -> Int -> Int; out-of-bounds -> -1), string_from_char_code (Int -> String; masks & 0xff)"
45+
detail = "Byte-oriented String<->Int accessors underpinning base64. Unlike string_get/int_to_char (Char-typed, ASCII-clamped), these go String<->Int directly so a byte 0..255 round-trips identically on interpreter + Deno-ESM. Wired through all four layers: resolver seed, typechecker binding, interpreter impl, codegen_deno __as_strCharCodeAt/__as_strFromCharCode + builtin map."
46+
47+
[changes.compiler.int-division]
48+
file = "lib/codegen_deno.ml"
49+
kind = "codegen-fix"
50+
issue = "#478"
51+
severity = "high"
52+
symptom = "Int/Int lowered to JS '/' (IEEE-754 float): 255/16 -> 15.9375, -7/2 -> -3.5. Broke math.affine pow/sum_naturals/binomial/lcm/div_floor and for-loop/indexed integer division on the Deno-ESM backend."
53+
root-cause = "Type-erased emitter mapped OpDiv -> \"/\" unconditionally; JS division is always floating-point."
54+
fix = "Lower a provably-Int a/b to Math.trunc(a/b) (truncate toward zero, matching interpreter OCaml / and wasm i32.div_s); every other / stays float. Conservative expr_is_int/expr_is_int_array classifier: int literals, Int params, let/assign-tracked Int locals (per-fn reset via enter_fn_scope, per-let-scope restore), integer-closed arithmetic, JS bitwise (int32), Int-returning fn/builtin calls, for-loop var over Array<Int>, xs[i] element read. Unknown operand keeps / (no float regression). x /= y over ints truncates."
55+
test = "tests/codegen-deno/int_div.{affine,harness.mjs} — truncation, toward-zero negatives, float preserved (incl. Array<Float> loops), loop-mutated locals, Int-returning-call operands, for-loop var, indexed array, /="
56+
remaining = "Sibling OpDiv -> \"/\" in lib/js_codegen.ml + lib/codegen_node.ml; wasm float-division mirror (codegen.ml uses I32DivS unconditionally); record-field Int operands. All tracked in #478. Complete fix = thread typechecker inferred types into backends."
57+
58+
[changes.config.gitignore]
59+
file = ".gitignore"
60+
kind = "chore"
61+
detail = "Ignore /.claude/worktrees/ — transient per-agent git worktrees, never committed (committing a nested worktree would corrupt the repo)."
62+
63+
[verification]
64+
method = "apt-bootstrapped OCaml toolchain (opam.ocaml.org is 403 in web env; Ubuntu apt packages satisfy pins)"
65+
dune-build-bin = "clean"
66+
dune-runtest = "pass"
67+
codegen-wasm = "pass"
68+
codegen-deno = "pass (incl. int_div + encoding_smoke)"
69+
face-transformer = "pass"
70+
check-no-extension-ts = "pass"
71+
check-doc-truthing = "pass (DOC-01..09)"
72+
fmt = "dune build @fmt is a no-op for lib/*.ml (no root .ocamlformat; ocamlformat self-disables); origin/main passes @fmt with zero proposals; verified with ocamlformat 0.26.2 (pinned)"
73+
not-runnable-locally = "full dune build / @doc (need js_of_ocaml + odoc; present in CI)"
74+
75+
[issue-state]
76+
umbrella = "#446 stays OPEN — STEP 1-3 done; STEP 4 (parallel tier execution) + STEP 5 (cross-campaign coordination) long-running by design"
77+
advanced = "bindings #19 shipped; stdlib #25 partial (hex+base64 done, Bytes overloads deferred to #30)"
78+
filed = "#478 (Int-division codegen; partially resolved — Deno-ESM done, js/node/wasm follow-ups tracked)"
79+
80+
[process-notes]
81+
hazard-1 = "Batching dependent edits caused silent edit-match failures + a stale-binary false-green + a misleading commit message. Corrected: one edit + one build at a time; amend with honest message; force-push-with-lease."
82+
hazard-2 = "Branch was 15 commits behind main; rebased before merge to avoid reverting landed PRs (CLAUDE.md branching discipline). Rebase surfaced new gate check-doc-truthing.sh, then satisfied."
83+
hazard-3 = "Earlier 'files CLEAN' fmt claim was a vacuous self-disabled pass; corrected on PR thread with the real proof (main passes @fmt; lib/ predates ocamlformat)."
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
= Bindings/stdlib roadmap closeout + hex/base64 + Int-division codegen fix
3+
:toc:
4+
:date: 2026-05-31
5+
:pr: affinescript#474
6+
:issues: #446 (umbrella), #478 (codegen bug, filed this session)
7+
:tests: codegen-deno (int_div + encoding_smoke added) · dune runtest · codegen-wasm — all green locally
8+
9+
A single-session unit of work that began as "drive the bindings-roadmap
10+
umbrella (#446) to conclusion" and grew, by following the work where it
11+
led, into: three roadmap-doc tracking fixes, one new spec doc, a new
12+
stdlib module with its compiler support, and a genuine codegen bug fix
13+
discovered + filed (#478) + resolved along the way. See the companion
14+
link:BINDINGS-STDLIB-CODEGEN-2026-05-31.a2ml[machine-readable A2ML] for
15+
the exact change inventory; this document is the human narrative.
16+
17+
== Trigger
18+
19+
The session opened on #446 — the *AffineScript top-50 framework bindings
20+
roadmap* UMBRELLA — with the prompt "drive this to conclusion." Auditing
21+
the umbrella's own STEP plan showed STEPs 1–3 already shipped (doc on
22+
disk, tier sub-issues #450–#454 filed, kickoff #455 closed), leaving only
23+
the explicitly long-running STEP 4/5. The concludable gap was a stale
24+
*Tracking* section in the roadmap doc that still read
25+
`(TBD — opened alongside this PR)` despite the umbrellas now existing.
26+
27+
That first fix expanded, at the user's direction ("both", "fan out",
28+
"do the rest"), across the sibling roadmap docs and then into real
29+
implementation work.
30+
31+
== What landed
32+
33+
. *Roadmap Tracking-section fixes* (3 docs). `docs/bindings-roadmap.adoc`,
34+
`docs/stdlib-roadmap.adoc`, `docs/alib-roadmap.adoc` each carried the
35+
same stale `(TBD)` placeholder. Filled in with the live umbrellas
36+
(#446 / #412 / #413) and their child-issue models — pre-filed per-tier
37+
for the bindings campaign, lazy (`stdlib #N` / `alib #N`) for the other
38+
two. Each doc's own rule is "do not let the table drift"; this honoured
39+
it.
40+
41+
. *Zig C-ABI FFI patterns doc* (bindings #19). New
42+
`docs/specs/zig-ffi-patterns.adoc` — the authoring recipe for binding a
43+
Zig C-ABI export to an AffineScript `extern fn`. Scoped against the
44+
`DOC-DEDUP` rule: SPEC §2.10 owns the grammar, STDLIB-EXTERN-AUDIT owns
45+
the inventory, codegen-environment owns the wasm codegen mechanics; this
46+
doc is the per-backend host-contract recipe that ties them together.
47+
Grounded in the real lowering (wasm `(import "env" "<name>")`; Deno-ESM
48+
same-named host symbol). Roadmap row `◐ → ●`.
49+
50+
. *Hex + Base64 encoding* (stdlib #25). New `stdlib/encoding.affine`:
51+
`to_hex` / `from_hex` / `to_hex_padded` / `hex_digit` / `hex_value`
52+
(lowercase 32-bit hex) plus RFC 4648 `to_base64` / `to_base64_url` /
53+
`from_base64` / `from_base64_url`. Built on the string-primitive layer
54+
and integer bit-ops; integer `/` deliberately avoided (see #478 below).
55+
Roadmap row `○ → ◑` (Base64 byte-oriented overloads await a `Bytes`
56+
type, stdlib #30).
57+
58+
. *Byte-primitive compiler support*. Base64 is byte-oriented, so two new
59+
builtins — `string_char_code_at` (String→Int, out-of-bounds → -1) and
60+
`string_from_char_code` (Int→String, masks & 0xff) — were wired through
61+
all four layers consistently: `resolve.ml` seed, `typecheck.ml` binding,
62+
`interp.ml` runtime, `codegen_deno.ml` `__as_*` helper + builtin map.
63+
64+
. *Int-division codegen fix* (#478). See below — the substantive one.
65+
66+
== The #478 bug: integer `/` lowered to float division
67+
68+
While writing the hex codec, a probe revealed that on the Deno-ESM
69+
backend `255 / 16` produced `15.9375`, not `15`: the type-erased emitter
70+
mapped `OpDiv -> "/"`, and JS `/` is IEEE-754 floating-point. This
71+
silently broke every integer-division consumer — `stdlib/math.affine`'s
72+
`pow`, `sum_naturals`, `binomial`, `lcm`, `div_floor` — on that backend.
73+
Filed as #478.
74+
75+
The fix lowers a *provably-`Int`* `a / b` to `Math.trunc(a / b)`
76+
(truncate-toward-zero, matching the interpreter's OCaml `/` and wasm's
77+
`i32.div_s`) and leaves every other `/` as plain float division, so
78+
`Float / Float` is untouched. Because `codegen_deno.ml` carries no static
79+
types, a conservative `expr_is_int` / `expr_is_int_array` classifier
80+
reports `Int` only when provable: int literals; `Int`-typed params;
81+
`let`/assignment-tracked `Int` locals (reset per function, restored per
82+
`let`-scope); integer-closed arithmetic; JS bitwise results; calls to
83+
`Int`-returning fns/builtins; `for x in xs` loop variables over a
84+
provable `Array<Int>`; and `xs[i]` element reads. An unknown operand
85+
keeps `/`, so float division is *never* silently truncated.
86+
87+
The for-loop and indexed-array cases were not in the first cut — a
88+
self-review agent caught them, and they were added before merge.
89+
90+
== Process notes (what went wrong, and the recovery)
91+
92+
This session is also a cautionary record of two self-inflicted hazards,
93+
both caught and corrected:
94+
95+
. *Batching dependent edits.* An early attempt applied the whole #478
96+
change as one large batch; three edits silently failed to match
97+
(wrong AST shapes — `StmtAssign` is a tuple, not a record; there is no
98+
`AssignMod`), the build broke, and a stale binary made "verification"
99+
look green. A broken commit was pushed with a message falsely claiming
100+
"all green". Recovery: revert to one-edit-at-a-time with a build after
101+
each, then amend the commit with an honest message and force-push. The
102+
lesson: never trust a test result without confirming the binary
103+
rebuilt; never narrate green without a fresh build.
104+
105+
. *Stale base.* The branch was 15 commits behind `main` at closeout time;
106+
merging would have reverted 15 landed PRs (they showed as spurious
107+
deletions in the diff). Rebased onto `origin/main` (clean, no
108+
conflicts) before merge, per CLAUDE.md's branching-discipline note. The
109+
rebase also pulled in a *new* CI gate (`check-doc-truthing.sh`,
110+
DOC-01..09) which then had to be — and was — satisfied.
111+
112+
. *Formatting gate, correctly understood.* `dune build @fmt` is the one
113+
CI step that could not be run locally (ocamlformat 0.26.2's non-GitHub
114+
deps are proxy-blocked). After obtaining the pinned version it was
115+
established that the step is a *no-op for `lib/*.ml`*: there is no root
116+
`.ocamlformat`, so ocamlformat self-disables, and `origin/main` itself
117+
passes `@fmt` with zero reformat proposals. An earlier "files are
118+
CLEAN" claim was a vacuous pass (the checker self-disabled) and was
119+
corrected on the PR thread rather than left to stand.
120+
121+
== Toolchain note
122+
123+
The web session reaches the Ubuntu archive and `github.com` but not
124+
`opam.ocaml.org` (403). Ubuntu's apt OCaml packages satisfy the project's
125+
pins, so a clean `dune build` / `dune runtest` was bootstrapped via apt —
126+
the same path the `.claude/hooks/session-start.sh` SessionStart hook
127+
(landed separately via #482) now automates for future web sessions.
128+
129+
== State after
130+
131+
* #446 umbrella: STEP 1–3 confirmed done; bindings #19 shipped, stdlib
132+
#25 partial. STEPs 4/5 remain long-running by design — the umbrella
133+
stays open. Per-row status updated in the roadmap docs.
134+
* #478: Deno-ESM backend fixed; the sibling `js_codegen.ml` /
135+
`codegen_node.ml` `OpDiv` and the wasm float-division mirror remain
136+
tracked there for follow-up. A complete fix would thread the
137+
typechecker's inferred types into the backends rather than re-derive
138+
Int-ness per backend.
139+
140+
== See also
141+
142+
* link:../bindings-roadmap.adoc[bindings-roadmap.adoc] — #19 row.
143+
* link:../stdlib-roadmap.adoc[stdlib-roadmap.adoc] — #25 row.
144+
* link:../specs/zig-ffi-patterns.adoc[specs/zig-ffi-patterns.adoc] — the
145+
new doc.
146+
* PR affinescript#474 — the landing PR (full per-workstream breakdown +
147+
verification matrix in the description).

0 commit comments

Comments
 (0)