Skip to content

Phase 1 / Etch / Async suspension core#39

Merged
guysenpai merged 15 commits into
mainfrom
phase-1/etch/async-core
Jul 1, 2026
Merged

Phase 1 / Etch / Async suspension core#39
guysenpai merged 15 commits into
mainfrom
phase-1/etch/async-core

Conversation

@guysenpai

Copy link
Copy Markdown
Contributor

Brief

briefs/M1.0.11-async-core.md — Status: CLOSED.

Summary

Delivers the Phase-1 async suspension core for the Etch tree-walking interpreter (criterion C1.6). The per-rule AsyncSlot becomes a dynamic AsyncTask pool carrying a resume frame-stack; a statement-head await suspends at any depth and resumes without re-running prefixes; async fn / async method execute via frame inlining; wait takes a Duration; and function coloring (E0901) + await placement (E0904) are enforced. race/sync/branch/spawn { }, wait_unscaled, entity_event, and handle-await remain fail-loud (owned by M1.0.12–M1.0.14). parser.zig/token.zig/ast.zig untouched.

Gated sub-steps (one review cycle each)

  • E1 — task pool + resume frame-stack (run/loop_/while_/for_/try_ frames); re-host async rule. for/try added after a review STOP (real EBNF v0.6 statement blocks, C1.6).
  • E2async fn / async method execution via call-frame inlining (heap-boxed scope + RetTarget; return resolves at the await site).
  • E3wait(Duration) via the fixed 1/60 timestep; E0904 placement (sub-expression + value-position await rejected, added after a review STOP).
  • E4 — function coloring E0901 (async call / await in a non-async fn/rule).
  • E5 — CLAUDE.md §3.4 + in-repo doc-comments.

Acceptance criteria

  • All E1–E5 Scope deliverables present
  • Out-of-scope constructs remain fail-loud (race/sync/branch/spawn { }, wait_unscaled, entity_event, handle-await)
  • Inline tests green (interp.zig async behaviors + types.zig E0904/E0901) — etch target 376 pass (376 total) in Debug AND ReleaseSafe
  • Observable behavior demonstrated by inline cross-tick tests (async fn n→42, method n→11, for n→1→3→6, try throw→catch, wait(1.0s)→tick 60) — the programs/ integration file was dropped (that corpus is the interp↔codegen differential; codegen rejects async — Recorded deviation)
  • Sync-only programs byte-identical to the pre-async runtime (no task pool, no async_tick churn — by construction)
  • zig build, zig build test (Debug + ReleaseSafe), zig fmt --check, zig build lint green
  • CLAUDE.md updated (§3.4: current-state → Next M1.0.12; +1 Tags row; M1.0.11 scope-boundary; "Last updated")
  • Language audit §3.6.1 — zero FR function-words on the branch diff + brief (empty grep)
  • Drift audit §3.6.1 — the M1.0.10 "Out" pointer spawn { } → M1.0.11 patched to M1.0.12; one historical tag-entry imprecision noted (Closing notes)
  • Brief Status: CLOSED, Closing notes filled

Closing notes

  • What worked: The explicit iterative frame-stack made resume trivial and side-effect-free — the stack persists across a suspension and re-driving it never re-runs a prefix (no double emit). Building the frame set COMPLETE up front meant E2/E3/E4 added no new AsyncFrame variants. Mirroring the sync executor's control-flow semantics in the driver, and reusing sync execStmt for leaf statements, kept the two executors aligned. E0904 reused the same stmt_head_await NodeId the interpreter keys on.
  • What deviated: Three FROZEN deviations (Claude.ai round-trips, tracked in the brief): §9.12 placement broadened to for/try/catch bodies (E1) and to reject value-position await (E3); the programs/ integration file dropped (codegen rejects async).
  • Flag for review: (1) the task pool never reallocates mid-drive and call-frame scopes are heap-boxed, so scope pointers survive suspension — M1.0.12 spawn must re-verify. (2) A bare async call in an async context (no await) is NOT E0901 and fails loud at runtime — its compile-time check completes in M1.0.12 with the effect-consumption constructs (CLAUDE.md open decision). (3) for-over-array/map with a body await inherits the M0.8 heap-across-suspend caveat (bounds-checked fail-loud, never OOB); the range form is unconditionally sound.
  • Measurements: No benchmark (per brief). Sync-only = byte-identical by construction. Etch tests 367 → 376.
  • Residual (intentional): bare-async-call-in-async compile check (M1.0.12); heap-iterable-across-suspend caveat; one historical tag-entry imprecision; the etch-reference-part1.md §9 KB re-upload (Claude.ai, post-merge).

Changelog

  • docs(brief): add / confirm specs / activate M1.0.11
  • feat(etch): async task pool + resume frame-stack (E1)
  • fix(etch): complete AsyncFrame set with for and try frames (E1)
  • feat(etch): execute async fn/method via await inlining (E2)
  • feat(etch): wait Duration + E0904 await placement (E3)
  • fix(etch): flag await in value-position blocks E0904 (E3)
  • feat(etch): function coloring E0901 async-in-non-async (E4)
  • docs(etch): document the Phase-1 async suspension model (E5)
  • docs(claude-md): update for M1.0.11
  • docs(brief): close M1.0.11

🤖 Generated with Claude Code

@guysenpai guysenpai merged commit 4daaf6c into main Jul 1, 2026
22 of 26 checks passed
@guysenpai guysenpai deleted the phase-1/etch/async-core branch July 1, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant