This is the work queue for autonomous agents (Codex loop / Claude). It exists so automation works on real, verifiable correctness and feature work instead of converging on packaging/hygiene busywork. It was seeded from the 2026-06-28 product review + 0.9.2 hardening sprint.
- Pull the highest-priority unchecked item (
[ ]) whose status is notIN PROGRESS. Work ONE item per branch. - Definition of done (every item): the fix/feature is implemented, new tests are added that would fail without the change, and the full suite is green —
cargo test --workspace --manifest-path turbo/Cargo.toml,cd turbo && ./tests/run_tests.sh,turbo/tests/parity/run_parity.sh, andcargo clippy ... -D warnings+cargo fmt --check. Update this file: check the box and note the commit. - No hygiene busywork. Do NOT open PRs whose substance is "keep X reproducible", "guard Y", "prevent drift", README/metadata polish, dependency bumps, or re-formatting — unless an item below explicitly calls for it. If you cannot make real progress on a backlog item, stop and leave a note here rather than inventing hygiene work.
- Honesty bar: never add a performance/benchmark/marketing claim that isn't backed by a committed, reproducible measurement (see the 0.9.2 benchmark correction). Use current evidence and methodology; older best-of results and the new paired evaluator are not interchangeable before/after measurements.
- Keep changes surgical and matched to existing style. One concern per branch; respect the file-ownership discipline if multiple agents run in parallel.
User-directed sequence: master plan, acceptance spec. Preserve prior BL history; BL-27B/BL-28 remain owned by their existing entries. The user paused the aggregate goal and explicitly requested no further goal execution on 2026-09-07. The authorized work is a bounded v0.16.0 release closeout, not continuation of later milestones. OMX CLI is not installed and no matching Circular project is exposed. No shadow OMX ledger has been fabricated.
Release-closeout checkpoint: JSON serializer concat intermediates and owned temporary inputs now reclaim in JIT/AOT, including the array serializer. The eighth diagnostic fixture verifies complete JSON output and zero live tracked allocations; full-size post-fix evidence reports 6,668,371 allocations/frees in each mode. Its one-pair timing and unequal JSON API paths are explicitly non-qualifying. Cargo reference artifacts are source-matched from build JSON, not guessed filenames. Fresh local gates: workspace, 358 integration/10 helper skips, 37 parity, normal/profile Clippy, fmt, C ASan/observer UBSan, five native allocation suites and 43 evaluator tests. Public roadmap/claims and v0.16 release metadata are prepared. G2.1 and all broader unchecked goals remain incomplete; release completion is not goal completion.
-
G1.1 — HTTP/SQLite JSON round trips. DONE in
e494051oncodex/g1-json-roundtrip. Three initial regressions failed before the fix; five CLI tests now cover byte-identical JIT/AOT serializer output, POST→GET→restart, port collision recovery and non-retry compiler errors. Fixed native struct/array/root string quoting and C control escaping, retained SQL binding, and corrected AOT body truncation at byte0x01. Fresh verification: workspace tests,350 integration passes/10 helper skips,37 native parity,11 existing WASM tests, C-runtime ASan, Clippy/fmt, C embedding smoke. Independentcode-reviewerAPPROVE andarchitectCLEAR after feedback fixes. Embedded NUL remains unsupported by the existing C-string ABI; no broader serializer/WASM/production qualification is claimed. -
G2.1 — Performance/allocation evaluator. IN PROGRESS on
codex/g2-evaluator. First slice: paired monotonic native-process collector, per-child RSS, hierarchical bootstrap, fail-closed qualification, retained raw evidence, independent output oracles and manifest preserving all11 planned cases. Only fib and wordcount are runnable in this slice; wordcount is labeled an application comparison because top20 algorithms differ. Allocation/RC/live-byte instrumentation, remaining fixtures/held-out corpus, in-program batching and cross-host evidence remain required. Controlled fixture forms remain pending G3, not silently passed. Seebenchmarks/EVALUATOR.md; all broader G1–G8 outcomes remain open.- Measurement foundation committed in
75f27e5;22 evaluator tests pass, including runtime stderr rejection, untracked provenance, checksum tampering, exclusive evidence creation and cross-process measurement locking. Independent code review APPROVE and architecture CLEAR. Native regression gates remain green (workspace,350 integration/10 helper skips,37 parity,Clippy/fmt). - Recorded diagnostic baseline:
benchmarks/results/g2-initial-20260906/,60 measured pairs/case across3 batches and281 retained process records; all output oracles pass. Qualification remains incomplete and the CPU subset fails the target. This is evidence for further work, not closure of G2.1 or a Rust-parity claim. - Next slice implemented: opt-in
allocation-profilecompiler flavor with one C accounting engine shared by JIT/AOT; normal builds compile out hooks. Shared-header allocation/data/header/live/peak/RC metrics, real arena reclamation and concurrent observer tests; calibration gives exactly1 allocation/32 data bytes/16 header bytes/1 retain/2 releases in each mode. Separate evaluator profile phase rejects invalid records and instrumented timing compilers. Whole-heap and reference-language coverage are still outstanding. A returning-entry wordcount smoke observed1,054,614 allocations and frees in each runtime with no live tracked allocations; not a whole-heap claim. - Observer/v2 fixture slice committed in
2c8d9f1:32MiB logical byte-buffer scan/update/checksum and2,097,152-operation integer/string hashmap churn, with pinned sources and independent oracles. Default-sized AOT/Rust smoke and small JIT/AOT/Rust checks pass;28 evaluator tests pass. Full normal workspace tests,351 integration/10 helper skips,37 native parity, Clippy/fmt and C ASan/UBSan pass. Code review APPROVE; architecture WATCH remains specifically for incomplete whole-heap/source/host qualification, not an identified code blocker. - Particle/v3 slice:10,000 seeded managed particles with32,768 exact dyadic fixed steps; safe Rust inline-AoS counterpart and independent closed-form integer oracle. Default AOT/Rust output checks pass; real-fixture JIT/AOT/Rust regression covers five parameter combinations through65,536 steps plus invalid input.31 evaluator tests, full workspace,351 integration/10 helper skips,37 native parity, Clippy/fmt pass. Independent code review APPROVE and slice architecture CLEAR; broader whole-heap/source/host WATCH is unchanged. Retained 512-step allocation diagnostic:5,130,018 shared-header allocations/frees in both modes, not a whole-heap or performance qualification. Five of11 fixtures are runnable; JSON/string/tree/SQLite/HTTP/worker and broader measurement coverage remain. G2.1 is not complete.
- Particle slice committed in
5e1c1ba. String/v4 slice adds1,048,576 visits over a pinned UTF-8 log-token corpus with explicit ASCII-margin/literal-replacement semantics and independent weighted-record oracle. Real-fixture JIT/AOT/Rust tests cover the golden corpus, alternate combining/ZWJ/mixed-script corpus and invalid steps;34 evaluator tests, full workspace,351 integration/10 helper skips,37 parity, Clippy/fmt pass. Default-sized native output smoke passes; independent code APPROVE and slice architecture CLEAR. Six of11 fixtures now run; JSON/tree/SQLite/HTTP/worker, full memory coverage and broader qualification remain. - Unresolved G1 native Unicode parity defects exposed during E2: JIT/AOT differ for
upper("Straße"),lower("É"), NBSP trimming and empty-separator UTF-8 splitting. Actual probe outputs and reproduction steps are retained in EVALUATOR.md. These must be corrected under the native semantic-parity work; the bounded token fixture does not certify or close them. No runtime behavior was changed by the string benchmark slice. - String slice committed in
ce06242. Tree prerequisite corrective slice: a two-node recursive struct aborted the compiler from unbounded inline cleanup generation, and recursive/forward enum payloads vanished during type resolution. Added two-phase nominal resolution, arity-preserving diagnostics and private recursive cleanup helpers without changing headers/field layout. Allocation regressions exposed and fixed unbalanced readonly-call references,??temporary ownership and payload-free enum-constructor ownership; late-argument reassignment is protected across named/method/UFCS/indirect calls, including mutable hashmap function-value adapters. Nested generic inference preserves the existing generic-array-index program and rejects inconsistent bindings/bound failures. Six200-round native ownership fixtures pass valid identical JIT/AOT profiles with zero live tracked allocations; separate function-value tests cover argument safety, not closure/environment reclamation. Fresh gates: workspace,358 integration/10 helper skips,37 parity, normal/profile Clippy, fmt, C ASan/UBSan,34 evaluator tests and all6 existing benchmark output oracles pass. Independent architecture CLEAR and final code review APPROVE after fixing review findings. Tree timing fixture is still pending, so the runnable count stays6/11; G2.1 and the aggregate remain open. - Tree prerequisites committed in
fb118a0. Tree/v5 fixture: sixteen seeded depth19 recursive enum trees,1,048,575 nodes each, with construction/postorder hashing/drop included. Rust uses safe boxed nodes; the independent oracle uses flat breadth-first construction and bottom-up reduction. JIT/AOT/Rust fixtures pass five small parameter combinations and invalid bounds. Profile tests require zero live allocations and identical counters, with the same peak for one versus four same-depth rounds; evaluator profiles enforce the zero-live contract and retain rejected raw records. The full-size diagnostic records16,777,204 tracked allocations/frees in each mode, zero live and peak1,048,575; its one-pair timing is explicitly not qualification.37 evaluator tests, workspace,358 integration/10 helper skips,37 parity, normal/profile Clippy and fmt pass. Independent architecture CLEAR and final code review APPROVE. Seven of11 fixtures now run; JSON/SQLite/HTTP/worker and broader G2/G3 qualification remain open. - Tree/v5 committed in
4d1054c. JSON extraction prerequisite: AOTjson_get(json_stringify(...))failed escaped-text round trips that JIT passed. Replaced the loose C lookup scanner with bounded complete-object validation, decoded UTF-8/Unicode-escape keys/values, last-duplicate selection and complete raw spans for nested values. Invalid escapes, malformed tails, invalid UTF-8, non-finite numbers and over-depth containers reject consistently in the covered corpus; long finite numeric tokens and underflow are accepted. Validation-only string scans allocate no decoded buffers; local numeric locales avoid global locale mutation. Shared decoder also fixes root-string escapes. C ASan and CLI serde-oracle/mutation/zero-live-ARC tests pass, plus workspace,358 integration/10 helper skips,37 parity, normal/profile Clippy/fmt and37 evaluator tests. Independent code APPROVE and architecture CLEAR. Canonical non-string formatting, raw-NUL ABI behavior, parse-once typed JSON values and the timed JSON fixture remain open; benchmark count is unchanged at7/11.
- Measurement foundation committed in
-
BL-1 — Finish retiring the
?.unwrap()panic class in codegen. Status: DONE (Claude, 2026-06-28, commit47bef1ce68adbc2be4640fc5ad8f012c06e81a6e). The 0.9.2 sprint fixed the 2 reachable panics but ~152compile_expr(...)?.unwrap()remain inturbo/crates/turbo-codegen-cranelift/src/builtins.rs(+5 insrc/expr.rs: ~1595/1620/1646/1851/1890). All are sema-guarded today, but a future sema gap panics the compiler (exit 101) instead of diagnosing. Convert them to?.ok_or_else(|| CodegenError { code: <fitting ErrorCode, e.g. E0400/E0403>, message })?. AC: no?.unwrap()on acompile_exprresult remains in those two files; add a fuzz/regression pass (extendturbo/fuzz/or add adversarial.tbs) that throws malformed-but-parseable input at codegen and asserts a clean diagnostic, never a panic. Done: converted all 152 sites inbuiltins.rsand all 5 inexpr.rsto?.ok_or_else(|| CodegenError { code: ErrorCode::E0400, message: "<fn>:produced no value during code generation" })?(context-specific messages).grep -c '?.unwrap()'is now0in both files. Coverage: 15 in-crate regression tests (builtins.rsmod panic_class_regression, run undercargo test) that drive the backend with sema bypassed and assert a gracefulE0400for every expr.rs site + representative builtins.rs sites; a 55-program deterministic sema-bypass corpus inturbo/fuzz/src/codegen_fuzz.rs(run_robustness_corpus, runs in CI); and 3 adversarial.tbs (tests/adversarial/bl1_unit_in_*) asserting a clean non-101 diagnostic end-to-end. Residual: 2 non-compile_expr.unwrap()s inbuiltins.rs(lookup_variant_tag_static(...).unwrap(), lines ~3351/3422) were intentionally left — they are infallible by construction (the variant tag was just resolved), not unwraps of a compiled subexpression. -
BL-2 — Add a real-world benchmark (CEO's top "next"). Done (commit 79129ef, merged to master). Added a word-count workload (
turbo/benchmarks/wordcount.tb+ C/Rust/Go baselines + deterministic input generator +run_wordcount.shrunner with warmup, best-of-5, and byte-for-byte output-equality enforcement). Measured on M5 Max: Turbo AOT ~240 ms vs C ~110 / Rust ~125 / Go ~130 — ~2.2x slower than C (further behind than fib40's ~1.3x). Published honestly in README + website with a reproduce command. Root-caused the gap to runtime hashmap/string handling → see BL-9.
-
BL-3 — f32 across the spawn/generic/closure ABI boundary. Done (commit
7e4a6d5, merged to master). Root cause was a single ABI disagreement:resolve_cl_typemappedf32 → F32for internal function signatures while every float value flows as F64 (f32 locals/arithmetic/returns already round-trip as f64), so the param var was declared F32 but fed an F64 value → Cranelift panic / closure + spawn miscompiles. Fix: promote f32 → F64 uniformly across the internal Turbo ABI (type_conv.rs), and preserve a real 32-bitF32only at the C FFI (extern) boundary via a newresolve_cl_type_ffithreaded throughcompile.rs. Removed the 0.9.2 sema rejects (reject_f32_params+ closure-param reject). New tests: 4 JIT↔AOT parity programs (f32_abi_{param,closure,spawn,generic}) + 5 phase1 value-assertion tests; removed the obsoletef32_{spawn,closure}_rejectERROR tests. Verified: 503 unit / 238 integration / 18 parity, clippy + fmt clean. No new semantic loss (the language never promised 32-bit rounding for f32). -
BL-4 — LSP scope/binding resolution. Done (commit 4ff0476, merged to master). New
turbo-lsp/src/resolve.rswalks the AST with a lexical scope stack mapping each ident occurrence (by span) to its declaration, respecting shadowing; rewired go-to-def, references, rename, hover, and a new document-highlight to be scope-precise (shadowed innerxno longer renamed with an outerx). Correct-over-complete: field-access-to-struct-field resolution still falls back (needs type inference). LSP tests 50 → 70. Follow-on (future): field/method resolution once a span→type map exists. -
BL-5 — AST-based formatter. Done (commit
677d243, merged to master). Replaced the line-based tidier with a real lex→parse→print pretty-printer (canonical operator/:/->spacing, block expansion, 4-space indent, trailing commas). Safety-gated: re-parses its own output and compares ASTs (behaviour- equality) + a comment multiset check; on any mismatch / unparseable input / block comments it returns the source byte-for-byte. Idempotent + semantics-preserving across all 204 phase1 + regression + adversarial programs;initscaffold passesfmt --check. turbo-formatter tests 8 → 36. -
BL-6 — Split the two god-functions. Done 2026-06-28 (sema commit
65a5ada, codegen commitd08fe36, merged to master).check_expr_inner(~4,540 lines) → a thin dispatcher delegating to 35 per-variantcheck_<variant>handlers (the ~2,800-line builtin section fanned out to 13 category helpers viacheck_builtin_call); longest fn nowcheck_matchat 370 lines. Thecompile_call_fallback arm (~311 lines, six concerns) →compile_call577→202 lines viacompile_enum_variant_ctor/compile_ufcs_method_call/compile_closure_call/compile_plain_fn_call(+compile_fn_call_args/infer_generic_ret_tty/try_inline_fn_call), all ≤84 lines. Zero behavior change — verified by identical ErrorCode/builtin-name/return multisets (sema) and JIT≡AOT parity (codegen); full suite green (267 integration / 28 parity). Holdout (out of scope, named): the pre-existingcompile_expr_innerExpr-dispatcher (1,347 lines) — BL-6 codegen was scoped tocompile_call's_arm; a future split could target it.
-
BL-7 — WASM backend feature gaps (closures). Done (commit
bedac76, merged to master). AC met: closures now compile to WASM and run underwasmtime, parity-matched to native. A closure is a heap{fn_ptr, env_ptr}pair; the body is lifted to a top-level Cfn(env, params)with captures in an env struct; clang lowers the fn pointers tocall_indirect. Works: direct closures, captures (int/bool/str/f64 via bit-pun), andmap/filterclosures. Fails loud (clean E0403, no miscompile): closures as user higher-order-fn params, and float-arraymap/filter. +5 WASM exec tests (the runner now does a 3-waywasm == native == .expectedcheck) +6 codegen unit tests; no native regression. Remaining WASM gaps (future): async, some match patterns, higher-order closure params, returning/storing closures, and the BL-27 Part B struct/array COW. -
BL-8 — Homebrew formula sha sync. Done (commit
f5f0bf5, merged to master). The real checksums are only known post-build andrelease.ymlalready writes the authoritative formula to theZVN-DEV/homebrew-turbotap, so the in-repo copy can't carry trustworthy shas. Marked it a non-authoritative template and extendedscripts/check_release_consistency.py(already run in CI via ci.yml + nightly.yml) to FAIL if placeholder all-zero shas appear WITHOUT the template marker (or if a marked template carries real-looking shas) — so the repo can never silently present fake-but-real-looking checksums. Network-free;--self-testcovers all three states + a mutation check. -
BL-9 — Runtime hashmap/string-key performance. Surfaced by BL-2. Done 2026-06-28. The str→int hashmap path was re-stringifying (
snprintf), re-parsing (strtoll), and re-allocating (strdup/free) the value on every increment — aget_intthen a separateset_int, each hashing the key. Fix: int values are now stored inline in the hashmap entry (taggedis_int+ivalue) in both runtimes (turbo_rt.cAOT +runtime.rsJIT), sohashmap_get_int/hashmap_set_intdo a single hash + single probe with no per-update allocation or stringification; str→str semantics are unchanged (hashmap_getstringifies an int entry on demand). Also added a fusedhashmap_inc(map, key[, delta]) -> intprimitive (the str→int counterpart of C'stable[k]++; single lookup-or-insert) wired through sema + JIT/AOT codegen. Measured (best of 5, ~5 MB, Apple M5 Max / macOS 26.5.1,run_wordcount.sh, output-equal): Turbo AOT ~150 ms vs C ~108 ms → ~1.4x (was ~240 ms / ~2.2x) — clears the ≤1.5x AC. The fusedhashmap_incis within run-to-run noise of the optimizedget_int/set_inton word-count (the win was the inline storage, not the second hash), sowordcount.tbis left UNCHANGED on the idiomaticget_int/set_intpattern — the published number needs no benchmark rewrite. JIT still round-trips strings forget_int/set_int(~205 ms / ~1.9x, ~unchanged). Microbenchmark:bench_hashmap_inc.tb(~5M increments, ~46 ms AOT). Tests:tests/phase1/hashmap_inc.{tb,expected}+ paritytests/parity/programs/hashmap_inc.tb(JIT≡AOT).
Seeded from a full /product-team cycle (strategist + e2e + frontend + backend + product-designer),
scoped to NEW/forward-looking issues (excludes the already-fixed 0.9.2 items and BL-1..BL-9). Each item
notes its source lane. Same rules apply (real tests, full green suite, no hygiene busywork, honest claims).
-
BL-10 — Struct field-assignment aliases storage (no COW) → silent data corruption. [backend] Done (commit
40ad9ef, merged to master). Structs already carried the same[cap][refcount]header as arrays but never consulted it on write. Newrt_struct_cow(in bothruntime.rsJIT andturbo_rt.cAOT) mirrorsrt_array_set'srefcount>1 → private copy → drop shared refdance;FieldAssigncalls it before the store and repoints the binding, with retains added at themut-param and array-element binding sites so the refcount is honest. Restores thedocs/SAFETY.mdCoW value-semantics guarantee for structs. +3 phase1 tests + 1 JIT/AOT parity test (struct_value_semantics); 241 integration / 19 parity green. Surfaced BL-27. -
BL-11 — Printing a compound value yields an opaque placeholder. [e2e] Done (branch
fix/bl11-compound-printing).print(arr)/ interpolating an array →[array]; structs →[struct P]; results →[result](only scalars + Optionals rendered). The official Collections tutorial visibly emitted[array]. Biggest day-1 debugging wall. Fix: extended the codegen-side recursive renderer (convert_to_strinturbo-codegen-cranelift/src/builtins.rs, the sharedto_str/interpolation path) to walk arrays ([1, 2, 3],[], nested), structs (Point { x: 9, y: 2 }— distinct fromto_json's{"x":9}), and results (ok(7)/err(reason)), mirroring the existing Optionalsome(x)/nonemechanism;print()now delegates all compound types to it. Strings render unquoted to matchsome("hi")→some(hi). Also fixed a latent bug where float/bool optional payloads rendered garbage. +4 phase1 tests + 1 JIT/AOT parity test (compound_printing); 245 integration / 20 parity green. -
BL-12 — Docs lie about syntax (
explainexamples + SYNTAX.md[Implemented]). [designer#1, e2e] Done (commit57bab40, merged to master). Corrected theenum/::snippets inexplaindocs E0200/E0303/E0307/E0316 totype/.syntax (each compile-verified viaturbolang run); relabeled fivedesign/SYNTAX.md[Implemented]section headers to[Partial]with per-construct[Planned]tags (array/slice patterns, default params, named args, inferred-arrow, array/nested destructuring, tuples, type aliases, turbofish,@derive(Debug/Serialize)— each status verified against the release binary), plus a legend pinning tags to "works on the current release binary."docs/errors.mdhad no code snippets (no change). Pre-existing quirk noted: aColor.Redto an undefined enum routes to E0300, not E0303 — left out of scope. -
BL-13 — Runtime/operational errors are second-class. [designer#2,#3] DONE. BL-13a (commit
57bab40): renderer no longer gluesmore info:onto an emptyHelp:label. BL-13b (commit64991cc, merged to master): added anE0601-E0611runtime/operational code range (fully documented;explainworks). Runtime traps (div-by-zero E0601, index OOB E0602, overflow E0603) print a styledruntime error[E06xx]:+Help:+more info:footer, byte-identical on JIT and AOT (twin helpers, TTY-gated color). Operational errors get the full colorederror[E06xx]:envelope — file-not-found E0611 + import E0610 drop the raw(os error N)jargon. WASM runtime traps left naked (separate backend) — minor follow-up. -
BL-14 — No in-browser "Try it"; playground gated behind install. [designer#4, strategist theme 2] DONE 2026-07-06: PR #27 merged (post-review fixes: output truncation instead of discard, JSON-escape-aware response cap, real FizzBuzz expected output, ALLOW_UNSAFE_HOST moved from the image to explicit per-deployment opt-in). Runner deployed to Fly app
turbolang-playground-runner(org zvn-dev, 2 machines iad, token secret set); Vercel production hasTURBO_PLAYGROUND_RUNNER_URL=https://turbolang-playground-runner.fly.dev/run+ token. Verified live:/healthzok, authenticated run returns stdout, tokenless requests 401,execrejected, andnpm run smoke:playground -- https://turbolang.devpassed against production.turbolang playgroundserves a working browser playground — but only after a CLI install. For a language, "run code in 5s" is the highest-converting action. AC: host the existing playground atturbolang.dev/playand add it as the hero's primary secondary-CTA ("Try in browser →") + a persistent nav item. Progress 2026-07-04 (branchcodex/bl14-browser-playground): added a static/playroute, homepage secondary CTA, persistent nav/footer links, sitemap coverage, and regression tests./playnow server-renders the default playground UI without a client-side rendering bailout while still hydrating shared?code=links. Added a same-origin/api/playground/runproxy contract that refuses local shell execution, rejects non-JSON content types, caps request JSON before parsing, validates source size/shape, and only forwards toTURBO_PLAYGROUND_RUNNER_URLwhen a separate sandbox runner is configured. Added a dependency-freewebsite/playground-runner/service + Dockerfile that runsturbolangthroughexecFilewith a fixed minimal child environment inside the documented container hardening boundary; startup now refuses missing/blankTURBO_PLAYGROUND_RUNNER_TOKENunless explicitly opted into tokenless isolated-local testing, configured tokens are trimmed, blank tokens are never accepted as bearer secrets, and startup no longer logs token-derived fingerprints. The runner now rejects excess concurrent executions with HTTP 429. Runner tests cover auth, HTTP/run, non-JSON content-type rejection, malformed UTF-8 request rejection, concurrency backpressure, failed execution stderr, SIGKILL-enforced timeout, oversized-output failure, JSON failure envelopes, and source-policy rejection for compile-time imports, unsafe/FFI features, plus host-access filesystem/process/network builtins includingargsandhttp_post_with_headerswhile allowing pure path string helpers; bundled examples are now regression-checked against that public-runner policy, and the collections example was smoke-run throughturbo/target/release/turbolang(Total: 29,Count: 4). The website proxy now returns explicit no-store / nosniff JSON, passes through safe runner validation/backpressure errors (400/413/429) instead of flattening them into a generic 502, while still hiding upstream server/auth failures behind proxy-owned errors. Added scriptable runner and public-site smoke probes (npm run smoke:playground-runner,npm run smoke:playground) that check/healthz,/play, safe execution, andexecrejection before deployment. Local public-site smoke againstnext startverified/playcontains the real playground UI, has no CSR bailout marker, safely executes code through the runner, and rejectsexec. Local smoke againstturbo/target/release/turbolangreturned stdout and enforced token auth. A configurednext startsmoke (TURBO_PLAYGROUND_RUNNER_URL=http://localhost:8790/run) successfully proxied/api/playground/runthrough the runner and returned Turbo stdout. Browser QA verified desktop and 320px mobile/playrendering, Run, example selection, copy/share fallback behavior, and fixed the cramped mobile header by hiding the external GitHub nav link belowsm. The website proxy now treats an HTTPS runner URL without a nonblankTURBO_PLAYGROUND_RUNNER_TOKENas incomplete configuration and does not make unauthenticated upstream runner calls. Full local gates passed:cargo fmt --checkfromturbo/,cargo test --workspace --manifest-path turbo/Cargo.toml,cargo build --release --manifest-path turbo/Cargo.toml,cd turbo && ./tests/run_tests.sh(272 passed, 0 failed, 10 skipped),cd turbo && ./tests/parity/run_parity.sh(29 passed), andcargo clippy --workspace --manifest-path turbo/Cargo.toml -- -D warnings. Local Docker image verification is blocked by an OrbStack data-image permission issue on this Mac, but PR CI now buildswebsite/playground-runner/Dockerfile, starts the resulting image, waits for/healthz, and runsnpm run smoke:playground-runneragainst the containerized service. That CI pass exposed and fixed a stalerust:1.86-slimbuilder image pin that no longer satisfies the current lockfile (home@0.5.12requires Rust 1.88), then exposed and fixed the missingdocs/errorspublic-docs tree thatturbo-cli/build.rsrequires during the runner image build. The public page deliberately does not proxy arbitrary source to a shell-running/api/run; BL-14 remains open until the runner is deployed behind the website and smoke-tested atturbolang.dev/play. -
BL-15 — Website SEO: duplicate metadata, no sitemap/robots/OG card. [frontend#1,#3] Done (commit
4482ae9, merged to master). Root layout setsmetadataBase(turbolang.dev) + atitletemplate; all 15 docs routes now carry unique titles + content-derived descriptions; addedapp/sitemap.ts(reads the docs dir from disk so it can't drift),app/robots.ts, and a generatednext/ogopengraph-image+ Twittersummary_large_imagecard. All 21 routes stay static SSG; lint + tsc clean. Per-pageog:title/og:descleft inheriting the brand default (noted as a one-helper follow-up). -
BL-16 — Commit the embeddable-typed-scripting wedge:
libturboC API spike. [strategist] DONE (commit0b3841c). The biggest gap is positioning, not code: as a "general-purpose compiled language" Turbo loses to Go/Rust on every axis. The one differentiated asset is a real Cranelift JIT. There is zerocdylib/staticlibin the repo. AC (spike): ship the codegen crate as acdylib/staticlib; minimallibturboC API (turbo_vm_new,turbo_eval(src), register one host fn callable from Turbo, call one Turbo fn from C, marshal int + string both ways); a runnable "C host calls Turbo, exchanges typed values, calls a host fn back" demo + docs; one line indesign/VISION.mdcommitting the north star. Target trusted/first-party scripts first (no sandbox). Separately time-box a sandbox-feasibility investigation (can the JIT be stripped of file/net/ syscall in a quarter?) → go/no-go, NOT a finished sandbox. Do this BEFORE any LLVM/perf or domain-sidecar work. Done:turbo-codegen-craneliftnow buildsrlib/cdylib/staticlib;libturbo.hexposes VM creation, host callback registration,turbo_eval, and zero-argi64/strTurbo calls;examples/libturbo-c-hostcompiles and runs a C host that exchanges typed values through registered callbacks. Docs indocs/libturbo.mdmark the current boundary as trusted first-party only and record the sandbox feasibility result: go for trusted embedding, no-go for untrusted scripts until the runtime symbol table becomes capability-shaped. Verified with the full backlog gates plus the CI-backedexamples/libturbo-c-host/smoke.shC host smoke.
-
BL-17 — AOT HTTP correctness (JIT divergences). [backend F2,F3] Done (commit
f929328, merged to master). F2:rt_parse_responsecolon-fallback now uses a strictrt_parse_status_u16(mirrors the JIT'sparse::<u16>()) — a non-numeric prefix is sent as a 200 body instead ofHTTP/1.1 0 OK+ colon-truncated body. F3: request bodies that don't fit the 16KB stack buffer are read into acontent_length-sized heap buffer (capped byRT_HTTP_MAX_BODY), mirroring the JIT'sread_exact— no more spurious431on >16KB POSTs; all existing guards preserved. +2 JIT/AOT parity programs (http_colon_response,http_body_limits); fail-before-fix proven by stashing the C change. -
BL-18 — Mutex can't express atomic read-modify-write. [backend F4] Done (commit
5a6b36b, merged to master). Addedmutex_update(m, closure)— the closure (fn(int)->int) runs UNDER the lock (lock → new = closure(old) → store → unlock), reusing the existing runtime-calls-closure pattern from the HTTP route handler (JITMutexGuardRAII; AOTpthread_mutex_lock/unlock). A shared counter is now correct under contention (4×25k → exactly 100000, 6/6 runs on JIT and AOT; get-then-set lost ~57%). Fixeddocs/stdlib.md+CONCURRENCY.mdto stop implying bare get/set suffice for concurrent read-modify-write. +phase1 +parity +2 codegen unit tests. Mutex values remain int-only (existing design); generic-T mutex out of scope. -
BL-19 — Sized integer types are an unusable island. [e2e] Done (commit
634a57a, merged to master). Added anascast operator end-to-end (lexer keyword →Expr::Cast→ sema → Cranelift JIT+AOT; Rust-style precedence between unary and binary; numeric↔numeric only,str as i32rejected via E0100; narrowing wraps two's-complement, float→int saturates toward zero). Extended untyped-literal coercion into annotated sized types for array literals ([u8] = [104,105]), struct-field init, and literal-operand arithmetic (n:i32; n+1). +3 phase1 tests +1 parity (int_casts); 248 integration / 23 parity. Deferred:to_i32-style builtins (superseded byas); literal coercion in comparisons + into float types; narrow array-element assignment. -
BL-20 — No CLI args (
args()is a stub). [e2e] Done (commit6b08cad, merged to master). AOTmain(argc, argv)→rt_set_args→rt_args()builds[str]fromargv[1..]; JIT uses aPROGRAM_ARGSglobal set by the CLI beforejit_run; clapRungained atrailing_var_argsoturbolang run f.tb -- a b c(and trailing args) forward to the program. Convention:args()[0]is the first user arg (binary/source path excluded); JIT≡AOT (run f.tb -- a band./bin a bproduce identical output). +dedicated args shell test (tests/args/, JIT==AOT, hyphen-leading + empty cases) +parity. Docs updated. WASMargs()untouched. -
BL-21 —
benchreports "0/N passed" on every valid benchmark. [designer#8, e2e] Done (commit13b3850, merged to master). Root cause was the parser rejecting@bench(unknown attribute), so BOTH the JIT and AOT bench paths failed. Made@bencha first-class marker attribute (FnDef.is_bench) accepted on every backend and round-tripped byfmt.benchreporting now leads with the timing, makes AOT-vs-JIT parity a separate non-fatal line (AOT parity: ok/…), labels by function name, and never prints0/Nfor a run that produced a valid timing (headline isN/M benchmarks completed). +@benchfixture +CLI integration test +TOOLCHAIN.mddocs.@benchis a marker only (noBencher/b.iter()API yet) — noted aspirational in the doc. -
BL-22 — Website conversion friction. [frontend#2, designer#9] Done (commit
533501e, merged to master). NewCopyButtonclient component copies a raw prompt-free command via a string prop (never DOM-scraped, so$markers can't leak); homepage install block stores clean commands with the$as aselect-none aria-hiddenaffordance. Installation leads with Homebrew; hero reduced to one primary CTA + an icon GitHub link; "Run the flagship demo" → "See" (links to static docs — no playground route exists yet; that's BL-14). All 21 routes stay static SSG; lint clean. Docs-page<pre>blocks hand-roll markup (no shared component) so copy buttons there were out of cheap scope. -
BL-23 —
stdlib.mdis missing ~18 working builtins. [e2e] Done (commita5d74fc, merged to master). Documented all 19 (str_to_int/str_to_floatwith theirResultreturns,sort,slice,random,random_range,pad_left/right,list_dir,mkdir,delete_file,file_exists,path_join,time_now,time_ms,format_time,exit,str_from_char,type_of) with compile-verified signatures read from the sema built-in env; added Filesystem + Date/Time sections. Count reconciliation: the brittle/ false "all 104 built-in functions" claims inREADME.md+GETTING-STARTED.mdwere softened to "100+" (drops the inaccurate exact count AND the false "all", since the docs still aren't exhaustive). stdlib.md still omits some builtins (substring, args, reverse, array_contains, any, all, int/float casts, math family) — a future doc pass, not blocking. -
BL-24 — Website a11y defects. [frontend#4-#7] Done (commit
6f105e1, merged to master). Single<main>per docs route (inner →<div>); sub-AAtext-gray-500/600small text → AA-passinggray-400(footer, benchmark labels, sidebar headers, captions; the onlygray-500left is the decorative aria-hidden$prompt); global:focus-visiblering using the real accent token;aria-labelon all 3 navs + a sr-only skip-to-content link;prefers-reduced-motionguard in globals.css. All 21 routes stay static SSG; lint clean. -
BL-25 — Long-running-server memory model. Done 2026-06-28 (code commit
8b3dcab+ docs, merged to master; Kirby chose "truthful fix + go deep"). The investigation found the README's leak warning was backwards: the per-request bump arena already bounds AOT servers (~1.8 MB RSS plateau), while the unbounded leak was on JIT (turbolang run—handle_http_connectionnever resetSTRING_ARENA, which only drains whenmain()returns) — and the flagship demo instructed exactly that JIT path. It also surfaced an UNDISCLOSED use-after-free: a stateful AOT server (a startup hashmap mutated in a handler) had its entries freed byrt_arena_end(). A1 — the JIT HTTP handler records a per-request arena high-water mark and truncates back to it after each response: RSS ~62 MB/1k-req leak → flat ~11 MB plateau (statelessweb-dashboardbyte-identical across requests; non-serverrununchanged). A2 — persistent hashmaps allocate entry storage with realmalloc/free(scope-following: request-local maps stay arena-backed → no residual leak), so server state survivesrt_arena_end(): ASan heap-use-after-free inrt_hashmap_inc→ clean, counter1,2,1,3…→1..10. JIT hashmaps needed no A2 (they own keys/values as RustStrings). +2 ASan-gated C-runtime regression tests + anexamples/stateful-counter/demo; JIT≡AOT parity preserved (28/28). Docs: corrected the backwards leak claim inREADME.md+docs/SAFETY.md(servers are bounded on both backends; residual narrowed to non-server infinite loops) and listed the stateful-counter example. Also fixed a latent CI red: BL-26's whole-float change madert_f64_to_str(-0.0)→"0.0"but the C-runtimetest_rt.cexpectation (run bytests.sh, which the standard local gate omits) still asserted"0"(commit28b0dfb) —tests.sh+ the ASantest_rt.cbuild are now in the local pre-push routine. A2 follow-on: the AOTchannel_queueusesturbo_calloc, so a channel created INSIDE a handler is arena-scoped (a startup channel is malloc-backed and fine) — not the BL-25 bug, left untouched; revisit for BL-16's embedded VM if in-handler channel persistence is ever needed. -
BL-27 — COW parity gaps surfaced by BL-10. [backend, follow-on] Two parts: native arrays (Part A, DONE) and the WASM backend (Part B, still open).
- Part A — native arrays (DONE, this commit). Confirmed that arrays had the same aliasing at two of three
sites: passing an array to a
mutparam andlet row = grid[0]; row[0]=…still aliased the source (onlylet b = aarray copy was correct). The array COW machinery already existed (rt_array_set'srefcount>1 → private copy+ theIndexAssignbinding repoint); only the refcount was dishonest because BL-10's retains were gated toTurboTy::Struct(_). Fix: widened both retain-site gates toTurboTy::Struct(_) | TurboTy::Array(_)— themut-param arg retain incompile_call(src/expr.rs) and the array-element extraction retain inlet(src/stmt.rs). No runtime change (rt_array_set self-sizes its copy). +2 phase1 tests (array_cow_mut_param,array_cow_nested_element) + 1 JIT/AOT parity test (array_value_semantics); JIT≡AOT verified on both reproductions. - Part B — WASM backend struct + array value semantics (STILL OPEN, deferred). The WASM C-transpiler
(
wasm_codegen.rs/turbo_rt_wasm.c) aliases on EVERY binding site for BOTH structs and arrays, and is a genuinely larger fix than Part A — deferred rather than shipped half-done. Confirmed underwasmtime:let b = a; b.x=99(struct) andlet b = a; b[0]=99(array) and array-mut-param all print the mutated value (e.g.99 99where native prints1 99); a struct passed to a fn does not even compile (void bump(long long p)vs avoid*arg → clang-Wint-conversion), and nested-array element extraction (let row = grid[0]) likewise fails to compile. Root cause (why it is bigger than Part A): (1) the WASM transpiler never emitsrt_retainat ANY binding site, so the refcount is always 1 →rt_array_set's COW never fires and there is no struct COW at all; (2) there is nort_struct_cowinturbo_rt_wasm.candFieldAssigndoes a direct store; (3) the stmt-contextIndexAssigndiscardsrt_array_set's return, so even with honest refcounts the COW copy's write would be lost rather than repointed; (4) the coarse string type-tags (var_types) don't track struct names (needed to sizert_struct_cow's field count) or array element types (needed to safely gate a retain ongrid[0]vs a scalarints[0]); (5) the struct/array param ABI is typedlong longwhile values arevoid*. Exact remaining scope: (a) fix the struct/array param ABI typing; (b) enrichvar_types/inference to carry struct name + array element type; (c) portrt_struct_cowtoturbo_rt_wasm.c(8-byte refcount header, no cap field) and call it inFieldAssignwith a binding repoint; (d) emitrt_retainat the three binding sites (let b = a,mut-param arg, array/struct element extraction), gated on the enriched types; (e) make the stmt-contextIndexAssignrepointobj = rt_array_set(obj, idx, val)whenobjis an lvalue; (f) addturbo/tests/wasm/value-semantics coverage + a WASM↔native parity case. Why deferred: a partial WASM change is either a no-op (no retains ⇒ COW never triggers) or actively unsafe (a mis-gatedrt_retainon a scalar treats an integer asaddr-8and corrupts memory — strictly worse than the current honest aliasing). Per the "don't make it worse" bar, the WASM gap is recorded here rather than half-fixed. - Known bounded leak (unchanged): BL-10 left a small bounded leak — a named struct (now also array) passed to a read-only callee is retained without a matching release (callee never COWs); the Part A array widening inherits the same shape but does not worsen it. Tighten if the embedded/long-running memory model (BL-25/BL-16) needs it.
- Part A — native arrays (DONE, this commit). Confirmed that arrays had the same aliasing at two of three
sites: passing an array to a
-
BL-29 — Harvest the surviving
automation/*branches. [triage done 2026-07-06] Of the twelve 2026-06-27 autonomous-agent branches, seven were verified superseded by master and deleted (local + origin). Five contain genuine gaps vs current master but were authored pre-refactor, so each needs re-porting, not merging:automation/hashmap-doc-truth-20260627-0811(semaBUILTIN_FNSmisseshashmap_set_int/hashmap_get_int— shipped builtins are unguarded against redefinition),automation/stdlib-import-truth-20260627-1011(STDLIB_MODULESlacksstd/fs/std/system/std/time+ many shipped builtins, soimport std/fs { path_join }fails today),automation/lsp-builtin-completions-20260627-1111(LSP completions omit all builtins; needs abuiltin_function_names()sema export),automation/fmt-directory-20260627-1411(turbo fmtcan't take a directory; re-port onto theturbo-formattercrate),automation/recursive-bench-discovery-20260627-1511(collect_bench_filesdoesn't recurse likecollect_test_filesdoes). Delete each branch once its gap ships. -
BL-26 — Error/CLI/runtime polish cluster. FULLY DONE 2026-06-28 (3 rounds). Weak/missing
Help:that should echo the actual signature/field list/missing variant (E0100/E0200/E0315); messages rename the user's type via aliases (i64→int) instead of echoing source spelling (E0110); import error doesn't teachimport { x } from "./m.tb";explainrejects100/e0100(normalize input); whole-number floats print as ints (ambiguous type); raw(os error 2)jargon in file errors;testsummary ordering/color/total-time; empty-RHSlet x =emits a misleading double-error; REPL spuriousunused variableacross lines; consider raw strings (r"…") so JSON/{-strings paste verbatim; empty[]can't infer. Backend P3s:rt_spawn_with_argsignorespthread_createreturn (joins uninitpthread_ton thread exhaustion);rt_format_timeuses non-reentrantlocaltime()(uselocaltime_r); JIT hashmap&mut *ptris a data race under concurrentspawnwrites. Hashmap-handle type confusion (pre-existing, found during BL-9): ahashmapis an opaque i64 handle, so sema does NOT reject assigning an int to a hashmap-typed var (e.g.m = hashmap_get_int(m, k)orm = hashmap_inc(m, k)) — the nexthashmap_*(m, …)then dereferences an integer and segfaults instead of producing a type error. Reproduces with pre-existing builtins too; the fix is a distinct opaque-handle type for hashmaps (and likely mutex/http handles) so int↔handle assignment is a clean sema error. (Thefmt:/->/operator-spacing gap here is already covered by BL-5.)DONE 2026-06-28 (contained high-value subset): E0100 arity
Help:now echoes the full signature + what was passed ('add' takes 2 args (a: int, b: int); you passed 1); E0200Help:names the missing variant(s) (add an arm 'Blue => ...' or a catch-all '_ => ...'); E0315Help:lists the struct's fields with a did-you-mean ('Point' has fields 'x', 'y' — did you mean 'width'?); E0110 now echoes the source spelling (i64, notint); malformed imports now teach the syntax (imports look like \import { sqrt, pi } from "./math.tb"`);explainnormalizes100/e0100/E100→E0100;rt_format_timeswitched tolocaltime_r;rt_spawn_with_argsnow checks thepthread_createreturn and fails cleanly instead of joining an uninitializedpthread_t`.DONE 2026-06-28 (Round 2 — 4 parallel branches, all merged to master, full suite green: 267 integration / 28 parity / workspace, clippy + fmt clean): raw strings
r"…"(lexer-only; reuses the existing string node so sema/codegen are untouched, brace re-encoding keeps interpolation literal — commitb526e8a); whole-number floats now print with a trailing.0(2.0not2, so the type is unambiguous; one shared JIT+AOT float helper + 14.expectedupdated; JIT≡AOT byte-identical — commit9bcf269); hashmap/mutex/http opaque-handle typing — int↔handle mixing (m = hashmap_get_int(m,k)/m = hashmap_inc(m,k)) is now a clean compile-timeE0111instead of a runtime SEGFAULT (exit 139→1), via a distinctTy::Handle(HandleKind{HashMap,Mutex,HttpServer})with one-wayint→Handlecoercion that preserves the legit "pass a handle to ani64param" idiom (commited4d266); CLI polish — remaining(os error N)IO leaks translated to plain language,testsummary gains TTY-gated color + total-time, and the REPL no longer flags a later-used binding asunused(commit87fd7fb).DONE 2026-06-28 (Round 3 — 4 parallel branches, all merged to master, full suite green: 271 integration / 29 parity / 7 WASM / C-runtime tests.sh, clippy + fmt clean): empty-
[]inference — a bare[]now adopts its element type from an annotated let/param/struct-field/return context (concrete-type guard keeps genuinely uninferrablelet xs = []/ generic[T]a cleanE0115; commitd93be28); empty-RHSlet x =now emits one clear diagnostic anchored on the=("expected an expression after=in let binding") — the literal double-error was already gone, the message was just misdirected at}/EOF (commitaabf0f0); WASM whole-float drift —turbo_rt_wasm.c'srt_format_f64ported byte-identical from the AOT rule, verifiedwasm == native == expectedunder wasmtime 43 (commita5386eb); JIT hashmap data race — the JIT hashmap is nowMutex<HashMap>behind the same i64 handle (onelock_hashmaphelper for all 10 ops, no reentrancy/deadlock, no escaping borrows); an 8-thread × 50k-inc stress repro went from 30/30 crashes → 30/30 correct (commitbc0170c). BL-26 is fully resolved. New finding while aligning WASM floats → tracked as BL-28. -
BL-28 — WASM backend mis-types index/field/unary float expressions as
int(value truncation). [found during BL-26 Round 3 WASM-float work] On the WASM target, afloatvalue reached via an array index (xs[0]), struct field (v.x), or unary negation (-3.0,-0.0) is mis-tagged asintbyinfer_type_taginwasm_codegen.rs(it falls through to"int"forExpr::Index/Expr::FieldAccess/Expr::UnaryOp), so it is printed viart_print_i64— which not only drops the.0but truncates the value (xs[1] == 2.5prints2,-3.0prints-3). Native (JIT/AOT) handles these correctly; this is WASM-only. Also mis-dispatchesto_str()of float arrays/structs tort_i64_to_str. AC: WASMinfer_type_tagresolves the element/field/operand float type (the WASM backend needs to track array element types + struct field types it currently doesn't) so these values print/serialize correctly; add WASM↔native parity coverage for float-via-index/field/negation. Distinct from BL-27 Part B (that's WASM struct/array CoW value semantics; this is scalar float type inference).
When all boxes are checked, STOP and ask for the next priorities — do not invent hygiene work.