POLYTONE — the AI-native programming language

Roadmap

POLYTONE grows in focused sprints. The timeline below is the real history — and the toolchain is green today.

Every phase is a branch — open one to see its sprints.
1–30Phases M1–M330/30complete

Sprints 1–30: the language (compiler, VM, browser runtime), five native formats with self-tested POLYTONE codecs, and six interactive pieces on this site — playground, canvas output, image editor, sound studio, video editor, web viewer.

  1. Sprint 1Lexer & spec v0.1shipped

    Tokens, indentation blocks, the keyword surface, and the first written spec.

  2. Sprint 2Parsershipped

    Full expression and statement grammar with teaching parse errors.

  3. Sprint 3Type systemshipped

    Local inference, bidirectional checking, strictness rules (no truthiness, no implicit conversions).

  4. Sprint 4VM executionshipped

    Stack-machine IR (PTIR) and an interpreter with value semantics and checked arithmetic.

  5. Sprint 5WASM scalar backendshipped

    Int/Float/Bool + print compiled to WebAssembly, differentially tested against the VM.

  6. Sprint 6Records & matchshipped

    Nominal product types, canonical constructors, patterns with enforced exhaustiveness.

  7. Sprint 7Standard preludeshipped

    Built-in methods on core types; everything fallible returns an Option.

  8. Sprint 8Enumsshipped

    Sum types with qualified variants, payload matching, exhaustiveness over variants.

  9. Sprint 9Function valuesshipped

    Lambdas as unnamed declarations, by-value captures, map/filter/fold.

  10. Sprint 10Test blocks & assertshipped

    Tests as a language construct, comparison asserts that report both operand values, ptc test.

  11. Sprint 11Modules & importsshipped

    Multi-file programs: import loads a neighbor file, pub fn is the export, access is always qualified (mathx.double).

  12. Sprint 12Type exportsshipped

    pub record and pub enum cross module boundaries: qualified types (shapes.Point), variants, patterns, exhaustiveness — nominal identity program-wide.

  13. Sprint 13Stdlib in POLYTONEshipped

    ints, lists, texts — written in the language itself, self-tested with test blocks, resolved via the stdlib search path.

  14. Sprint 14Bytes & file I/Oshipped

    Binary buffers with value semantics, read_file/write_file as Results — and POLYTONE's first real image (binary PPM).

  15. Sprint 15.pti — the first native formatshipped

    The format doctrine (text never binary, intent over samples) and .pti v1: canvas + drawing ops, decoded by POLYTONE-written stdlib code, exported through the single PPM bridge. Next: .pta audio (16), .ptm 3D (17), .ptw web (18).

  16. Sprint 16.pta — native audioshipped

    Tempo, voices, note patterns as canonical text; a POLYTONE-written synthesizer renders to PCM through the single WAV bridge. Next: .ptm 3D (17), .ptw web (18).

  17. Sprint 17.ptm — native 3Dshipped

    Primitives with placement plus raw triangles as canonical text; a POLYTONE-written tessellator renders to triangle meshes through the single OBJ bridge. Next: .ptw web (18).

  18. Sprint 18.ptw — native webshipped

    Documents as a typed block tree (Block is a pub enum), rendered by exhaustive match with escaping by construction — the single HTML bridge. Phase M1 complete: all four native formats live.

  19. Sprint 19Image toolkitshipped

    crop, flips, scale, blit, and map_pixels with lambdas — plus invert/grayscale/brighten. Two new VM fast paths took the pipeline from 105s to 0.6s. Next: audio toolkit (20), .ptv video (21), language pass (22), CLI (23).

  20. Sprint 20Audio toolkitshipped

    silence, tone, append, mix, gain, repeat, and ADSR envelopes — music composed in code, exported via the WAV bridge. Next: .ptv video (21), language pass (22), CLI (23).

  21. Sprint 21.ptv — native videoshipped

    Named scenes (in the .pti grammar) plus a play/fade timeline, rendered through the image codec to a directly playable Y4M stream. Five native formats complete. Next: language pass (22), CLI (23).

  22. Sprint 22Language passshipped

    Copy-on-write values (sharing until mutation — value semantics, paid lazily), match guards (case Some(n) if n > 0:), and type re-exports across modules. Next: media CLI (23).

  23. Sprint 23Media CLIshipped

    args() and env() builtins plus ptc run <file> [args...] — and ptrender, the universal renderer for all five native formats. Phase M2 complete. Next: the memory-managed WASM runtime (24).

  24. Sprint 24The browser runtimeshipped

    The reference VM + compiler front end compiled to WebAssembly — full POLYTONE in the browser, sandboxed VFS, embedded stdlib, one canonical semantics. Next: the live playground (25).

  25. Sprint 25The playgroundshipped

    The docs site runs POLYTONE live: editor, curated examples, run/test modes, media previews (PPM→canvas, WAV→audio, HTML→iframe), share links — and a fuel bound that turns infinite loops into teaching errors. Next: canvas output (26).

  26. Sprint 26Canvas outputshipped

    Written files are the output system: .y4m plays frame-exactly on a canvas player, .ppm repaints in place, and Live mode re-runs code as you type. No browser-specific API entered the language. Next: image editor (27).

  27. Sprint 27Image editor v0shipped

    Every click runs real POLYTONE in the sandbox; the session exports as a .pt program that reproduces the picture byte-exactly. The stdlib gained from_ppm_bytes, the bridge's inverse. Next: sound studio (28).

  28. Sprint 28Sound studio v0shipped

    A step sequencer whose document IS .pta: live-updating native text, chords as extra voices, sandbox rendering, and exports as .pta, .wav, and a byte-exact .pt program. Next: video editor (29).

  29. Sprint 29Video editor v0shipped

    The document IS .ptv: scene cards with live sandbox thumbnails, a reorderable play/fade timeline, in-page Y4M playback, and exports as .ptv, .y4m, and a byte-exact .pt program. Next: the POLYTONE web viewer (30).

  30. Sprint 30The POLYTONE web viewershipped

    The pragmatic own browser: address bar + history over a site of native documents; .ptw pages interlink via native addresses, and .pt addresses run as apps — args in the address, document out. Phase M3 complete.

31–36Phase M4 — Depth & ecosystem6/6complete

Language ergonomics first (record-update, moves, generics), then format v2 revisions written in the better language (.ptw inline media, .pti gradients), then ptpkg — packaging once there is something worth packaging.

  1. Sprint 31Record-update + movesshipped

    with (spec §23) shipped with full teaching errors; move analysis + a fused in-place field write make the set_pixel rebuild chain zero-copy: 5.05 s → 0.068 s (~75×). Next: generics I (32).

  2. Sprint 32Generics I — functionsshipped

    Type parameters with total call-site inference, opaque-once body checking, transitive monomorphization to readable instances (index_of[Int]) — the VM never sees a type parameter. Stdlib: index_of, reversed, take, drop. Next: generics II (33).

  3. Sprint 33Generics II — records & enumsshipped

    record Pair[A, B], enum Tree[T]: total construction inference with left-to-right sharpening, unit variants via the expected type (like None), qualified across modules, recursion included — the VM never sees a type parameter. Next: .ptw v2 (34).

  4. Sprint 34.ptw v2 — inline mediashipped

    image/film/sound blocks with native addresses; to_html_bytes(doc, assets) embeds images as BMP data URIs (encoder in POLYTONE), the viewer hydrates film/sound into players, v1 keeps rendering. Next: .pti v2 (35).

  5. Sprint 35.pti v2 — gradients & palettesshipped

    palette: section, gradient fills with exact endpoints, even-odd polygons — plus toolkit twins and the editor's gradient/triangle tools with a palette row. v1 renders byte-identically. Next: ptpkg v0 (36).

  6. Sprint 36ptpkg v0 — packagesshipped

    polytone.pkg manifest with pkg.render as teaching decoder, ptc vendor with local → vendor → stdlib resolution, the imaging-extras example, and the Registry page. Phase M4 complete.

37–42Phase M5 — The depth pass6/6complete

Sprints 37–42, complete: ptc fmt + doc, image editor v1, .pta v2 + studio v1, .ptv v2 + video v1, .ptw v3 forms, playground v2 — every deepening landed as a format revision first, tool second.

  1. Sprint 37ptc fmt + ptc docshipped

    Token-stream formatter (comment-preserving, idempotent, refuses on token drift; the corpus is canonical), .ptw v2 API pages from /// docs — plus Map/Text/Bytes iteration, text[i], and block lambdas in binding position. Next: image editor v1 (38).

  2. Sprint 38Image editor v1shipped

    Layers as op groups (the flattened session IS a valid .pti v3 document — byte-identical, verified), drag brush, fill op, zoom, op-level undo, .ppm import via from_ppm_bytes, and the draw_ops engine. Next: .pta v2 + studio v1 (39).

  3. Sprint 39.pta v2 + sound studio v1shipped

    pattern blocks + song: chain (sample-exact), swing, per-voice ADSR (default = v1 shape), ! accents — and the studio's tracks, envelope presets, pattern chips, and 16/32 steps. Next: .ptv v2 + video v1 (40).

  4. Sprint 40.ptv v2 + video editor v1shipped

    Sprite blocks + move tweens (pixel-exact endpoints), the text op via .pti v4's built-in 5×7 font, audio: references resolved into a .y4m+.wav pair — and the editor's sprite cards, move rows, and soundtrack editor. Next: .ptw forms (41).

  5. Sprint 41.ptw v3 — apps with inputshipped

    input/button blocks, fields as name=value args on the button's app address (state rides the address), web.form_value, and the guestbook demo — verified end to end. Next: playground v2 (42).

  6. Sprint 42Playground v2shipped

    Multi-file tabs with a modules example, a highlight overlay, error markers that switch to the failing tab and line, and whole-file-set share links. Phase M5 complete — next: Phase M6 toward v1.0.

43–48Phase M6 — Platform & v1.06/6complete

The live static registry (this site serves it), LSP v0, .ptm v2 + a POLYTONE-written 3D viewer, native ptc downloads for Windows/macOS/Linux, the generated API reference — then CI gate, spec audit, and the spec-v1.0 compatibility promise. The toolchain stays 0.x through the beta; versions read 0.PHASE.SPRINT.

  1. Sprint 43The live registryshipped

    index.ptr + pkg.render_index, /registry/ served by this site through the deploy chain, and ptc vendor <url> <name> fetching index-checked packages over HTTPS with teaching errors. Next: LSP v0 (44).

  2. Sprint 44LSP v0shipped

    polytone-lsp over stdio: live diagnostics from the real front end (unsaved-buffer substitution through the module loader), hover with signatures + docs, golden-tested sessions, editor setup documented. Next: .ptm v2 + the 3D viewer (45).

  3. Sprint 45.ptm v2 + the 3D viewershipped

    Colored shapes (palette + color, the .pti grammar) and mesh.render_view — a software rasterizer written in POLYTONE (orbit camera, painter's algorithm, two-sided shading) — plus the site's 3D viewer: drag to orbit, exports .ptm/.obj/.pt. Next: the generated API reference (46).

  4. Sprint 46ptc for your machineshipped

    Native toolchain builds for Windows, macOS, and Linux — ptc + polytone-lsp with stdlib and examples, compiled by CI on real runners on every version tag, delivered through the deploy chain, with a download page and install steps. Next: the generated API reference (47).

  5. Sprint 47The generated API referenceshipped

    ptc doc over every stdlib module, parsed from the toolchain's own .doc.ptw output into the site's API section: 76 pub items across 9 modules with signatures, docs, filter, and search — generated from the code that ships, so it cannot drift. Next: CI gate, spec audit, the compatibility promise (48).

  6. Sprint 48CI gate, spec audit, the promiseshipped

    ci.yml gates every push (compiler tests, clippy, the canonical-formatting corpus, all 81 stdlib self-tests, runtime smoke, site build); the spec survived a machine-checked drift audit (every code block through ptc check) and graduated to v1.0 with §27, the compatibility promise. The toolchain stays 0.x through the beta — 0.6.48.

49–54Phase M7 — Post-1.06/6complete

Package dependencies + ptc pack, the full-value native WASM backend (differentially tested against the VM), ptc bench with a tracked suite, and agent-native tooling: machine-readable diagnostics, an MCP server, and the agents guide.

  1. Sprint 49Package dependenciesshipped

    Manifest v2 adds deps:; registry vendoring resolves transitively (depth-first, cycles teach the chain, conflicts name both versions) and vendor.lock records name, version, needed-by, origin. poster-tools is the live example. Next: ptc pack (50).

  2. Sprint 50ptc pack — the publishing gateshipped

    One command validates a package end to end — strict manifest, fmt --check, a /// doc on every pub item, every test green (at least one required) — and prints the exact index.ptr line to paste. Both example packages pass their own gate in CI. Publishing stays a git push; the gate makes it safe. Next: native codegen I (51).

  3. Sprint 51Native codegen I — values in memoryshipped

    ptc build compiles full-value programs: records, enums, collections, Text, match, generics, multi-file. Real WASM functions own control flow, calls, and recursion; every value operation runs in the committed value runtime — the VM's own exec_simple compiled to WASM, values refcounted in linear memory. The differential suite (14/17 fixtures byte-identical, error parity incl. positions) is a CI gate. Next: closures & CoW (52).

  4. Sprint 52Native codegen II — closures & CoWshipped

    The whole language compiles: closures (captures included) dispatch through the module's function table via call_indirect, args()/env() and file I/O reach the host through four runtime imports with the ptc run contract, and CoW moves run the VM's own fast paths. All 17 fixtures compile and match the VM byte for byte — the differential CI gate is total. Next: ptc bench (53).

  5. Sprint 53ptc bench — the performance passshipped

    ptc bench runs ordinary .pt programs (codecs, rasterizer, the CoW rebuild chain) and reports deterministic instruction counts plus wall time; --record keeps history.jsonl in-repo, --check gates CI on ops regressions. The first pass, judged by the suite: per-row/per-column gradient interpolation, −41% ops total, byte-identical output — and two VM optimizations that showed no win did not land. Next: agent-native tooling (54).

  6. Sprint 54Agent-native toolingshipped

    ptc check --json emits the polytone-diagnostics v1 document — structured teaching errors with phase and 1-based positions, fields additive-only; ptc check resolves imports now. polytone-mcp exposes check/run/test/fmt/doc/render as MCP tools over stdio. Both sit on the new polytone-driver library — the same machinery the IDE loop will drive (55+). The agents guide documents the contract.

55–62Phase M8 — POLYTONEide8/8complete

The commercial IDE that rethinks the category around what POLYTONE uniquely owns: a verification loop with a human window. ptc context compiles minimal prompts, the sandbox verifies before humans see code, the token ledger proves the savings — subscription for the machinery, your own AI keys for the tokens.

  1. Sprint 55POLYTONEide — the workbenchshipped

    ide/ joined the monorepo as a self-contained package: project tree derived from paths, tabs, the playground-proven overlay editor, run/test against the committed wasm runtime with teaching-error line jumps, projects in IndexedDB (autosave + restore), local folders via File System Access with an import/download fallback. Deployed under /ide/, unlinked until 62. No AI yet — the loop lands in 57 on the Sprint-54 substrate.

  2. Sprint 56ptc context — the context compilershipped

    ptc context emits the polytone-context v1 document: the file's own items plus the pub surface of its direct imports as structured signatures with docs, diagnostics as payload (repair context), and — at a position — the one enclosing item with its body. Never other bodies, never the prelude (that lives in the cached language card). Also the seventh polytone-mcp tool; frozen additive-only like polytone-diagnostics. The verified loop consumes it next (57).

  3. Sprint 57The verified loopshipped

    The workbench generates now: intent → context slice (polytone_context joined the browser runtime, same driver machinery as ptc) → sandboxed check + tests → bounded teach-repair, where the repair prompt is the teaching error line → a diff marked verified or not, with Apply/Discard. Anthropic/OpenAI/compatible adapters, BYO keys in localStorage only, and a local token ledger that counts every run. Golden-tested against the real wasm with a scripted provider.

  4. Sprint 58The intent ledgershipped

    Intents are first-class records now: each run carries its PLAN line, a multi-file change-set (path-labeled blocks) with before/after, status, and token cost — the ledger derives from them. Plan/diff/apply UX with a status-chip history, and sessions export as replayable polytone-session files that re-verify locally on import: a replay never calls a model, an unverifiable replay never applies.

  5. Sprint 59Routing & the benchmarkshipped

    The cheap model tries first and escalates only on failed verification — local checks are free, so failed cheap attempts never cost strong tokens; rounds and tokens attribute per tier. The frozen language card ships as a cacheable system block. And benchmarks/context is the published, CI-gated slice-vs-file-context benchmark with honest numbers: a two-file toy is a wash, a program importing images is 7.6× fewer tokens per request.

  6. Sprint 60Media-native developmentshipped

    Native documents are first-class in the IDE: a live preview pane renders the open .pti/.pta/.ptm/.ptw/.ptv through its sandbox codec as you type, with teaching errors in place. Intents can target documents — a doc that does not render fails verification like a module that does not compile, and the codec's error is the repair prompt. Run outputs preview inline: PPM canvas, Y4M player, WAV audio, HTML iframe.

  7. Sprint 61The product shellshipped

    Pro keys are ECDSA-P-256-signed and verify offline against an embedded public key — no account, no activation server, no telemetry. The gate is honest: Free keeps the whole verified loop; Pro unlocks routing, session export/replay, and the full history. First-run onboarding states the product and the privacy stance in four sentences, and this site gained the POLYTONEide docs page. Hosted checkout lands with 1.0.

  8. Sprint 62The launch surface, early accessshipped

    The product page carries the measured benchmark numbers and pricing (Free 0 €, Pro ≈ 12 €/month intended, Team later). The workbench is deployed at /app/ behind the early-access gate: a password checked as a salted hash — only the hash lives in the repo — with the unlock sticking per browser. Public launch, hosted checkout, and the desktop (Tauri) beta follow when the product flips public; 1.0.0 stays reserved for that moment. You pay your AI provider for tokens — and POLYTONEide for needing fewer of them.

63–70Phase M9 — The consolidation pass8/8complete

Honest inventory before anything new: eight phases shipped fast, and speed leaves residue. M9 builds nothing for the launch — it makes what exists true: every claim in the docs verifiable, every surface tested, every audit finding fixed or honestly recorded. It opens with a review round, area by area, against real screenshots — the confirmed findings fill the sprints. The launch, hosted checkout, the desktop beta, and registry accounts are all M10.

  1. Sprint 63One bridge, one highlightershipped

    The byte-copy era is over: the runtime bridge, the highlighter, and the y4m decoder live once in polytone-web-shared (runtime/web/), imported by web/ and ide/ through one root workspace with one lockfile. The CI drift check and the wasm-sync script retired — the wasm is a declared build input. The reconciliation also swallowed the unenforced near-duplicates: the PPM decoder existed five times, now once. First real unit tests at the source: protocol round-trip against the committed wasm, highlighter token classes with the known quirks pinned, the decoders, error positions.

  2. Sprint 64The web test harnessshipped

    web/ has a test script for the first time: Vitest on the existing vite config, 23 tests — the share-link codec extracted to a pure seam with the review holes fixed, search/SEO/i18n invariants, a roadmap↔ROADMAP checkmark-parity gate, registry↔index.ptr equality, and every curated example against the committed runtime with expected-fail flags. CI gates it — and the parity gate caught its first real drift on arrival: the missing checkmarks at sprints 46 and 49.

  3. Sprint 65Crate depthshipped

    Cargo is at 586 tests (+100): real suites for ptir (codec round-trips, exhaustive corrupt-input rejection, lowering goldens), driver (every entry point), wasm-rt (host interface, CoW edges), and ast. The fixture corpus grew to 23 — recursive harnesses enforce the modules/ trio, and the differential gate runs 21/21. MCP survives malformed JSON, the LSP has a real stdio smoke, hostile lengths no longer abort the wasm module, every production expect() carries its justification, and the stdlib self-tests grew to 91.

  4. Sprint 66Codegen: the arc, closed and provenshipped

    The truth held — the arc really closed in 51/52 — so this sprint hardened it: the differential corpus drives all five media codecs through ptc build (22/22), compiled-vs-VM bench tracks report wall time with asserted-identical outputs, and the CI-enforced wasm freshness gate makes a stale committed runtime impossible. §9 no longer contradicts §12. The teaching-error pass: arithmetic traps, key-not-found, ptc's IO errors, and 'expected X, found Y' all name their fix now. ptc render closes the CLI↔MCP symmetry.

  5. Sprint 67async means somethingshipped

    The keyword that parsed for 66 sprints without meaning has its decided semantics: calling an async fn runs nothing — it captures the arguments into a Task[T]; .run() is the only way anything happens; tasks.all (new stdlib module, in POLYTONE) drives a list in fixed order. No scheduler, no await — determinism is the feature. A Task lowers onto the closure machinery, so VM and compiled backend execute identical deferral with zero new instructions (differential 23/23). Teaching errors guard every misuse; spec §28; the LSP shows async; guide + playground example ship with recorded outputs.

  6. Sprint 68Media tools: the findings passshipped

    Every confirmed finding fixed with a regression test (the web harness is at 52). The four S1s: v4 thumbnails, corruption-proof snapshot undo, the film .pt embeds its soundtrack, Enter submits the right form. Sessions reopen everywhere (.pti/.pta/.ptv with teaching rejections), the codec surface is reachable (text tool, N-gons, custom ADSR, 8 tracks), undo exists in all three tools, the format docs tell the truth again, the OBJ bridge's geometry-only nature is documented (bytes frozen per §27), and the performance numbers come from one pinned benchmark instead of ad-hoc quotes.

  7. Sprint 69IDE + site: the findings passshipped

    The verified loop's five S1s are closed: no writing outside the project, the target pick survives, the verdict parses the test summary (not the word FAILED), provider errors keep their spent tokens in the ledger, and applied no longer counts as verified. Token honesty, no-op change-sets dropped, zero-byte renders rejected; the IDE suite grew 43→60. On the site: Error Lab subpages keep their title/description on hydration, polytone-mcp appears everywhere it was missing, static fallbacks are current, seven dead i18n keys gone (web harness 58). The LSP formats now (F1.9); a jsdom deploy smoke mounts both apps before FTPS (F7.2); the context benchmark re-runs at 0.9 (8.0× on stdlib-media).

  8. Sprint 70The true recordshipped

    The bookkeeping pass that closes Phase M9: the spec footer, §19 rewritten to the real nine stdlib modules (so §27's freeze covers what it promises), §8.4 and §10 corrected, CONCEPT status realized, the CLAUDE.md phase line, the M6/M7/M8 phase-complete markers. The five git tags orphaned by the 0.x renumbering are restored. Release safety: publish no longer ships a manifest for a failed build, CI/release gain concurrency guards and a declared toolchain. And a docs-record CI gate ties the spec footer, the phase line, and the roadmap checkmarks to the shipped version — this class of stale-status finding can never silently reopen. Phase M9 complete.

71–74Phase M10 — Launch3/4in progress

The machinery that turns POLYTONEide from an early-access page into a launched product, staged so the go-live is a single deliberate flip, not an accident. The launch switch, hosted-checkout scaffold, and Tauri desktop shell all land flag-off — a green build ships nothing public until LAUNCHED = true. The flip itself is a 0.x event (not 1.0.0 — that milestone is reserved for a serious product on the market), gated on the external ops only the owner can do: the Paddle product, the deployed checkout endpoint, signed desktop builds.

  1. Sprint 71Launch switch + hosted checkoutshipped

    Phase M10 opens, flag-off: one shared LAUNCHED/CHECKOUT_URL constant in polytone-web-shared drives the whole reversible surface (a single source, not a hand-synced twin); a checkout/ scaffold documents the Paddle flow (checkout → webhook → sign-license → email, the signing key in the environment, never the repo); the license note reads the switch. Nothing public ships while the flag is off.

  2. Sprint 72Tauri desktop shell — beta scaffoldshipped

    The same workbench in a native window: a standalone Tauri v2 crate (ide/src-tauri/, not a compiler-workspace member) wraps the built frontend; core/platform.ts isTauri() boots the desktop binary past the early-access gate; desktop.yml builds installers on a desktop-v* tag. Native FS and keychain key storage are documented fast-follows.

  3. Sprint 73Launch-flip surfaceshipped

    The coming-soon banner, the product-page CTA, the download lock, and the IDE boot gate all read the one shared flag through pure helpers; four tests prove the flip flips the whole surface without flipping the real flag (which stays false).

  4. 74
    Sprint 74The flip — a 0.x launchnext up

    The single deliberate go-live commit: LAUNCHED = true plus its companions — delete the noindex meta, bump to the launch sprint's 0.x version, record the launch across footer/CLAUDE/ROADMAP/CHANGELOG. Not 1.0.0: the launch is a beta-scheme event; 1.0.0 is a later, market-triggered milestone with its own graduation (the footer drops its sprint span, the docs-record gate learns the post-beta form). Gated on the external ops only the owner can do: the Paddle product, the deployed checkout endpoint, signed desktop builds.

75–81Phase M11 — Capabilities7/7complete

M10 sells what POLYTONE is good at; M11 widened what that is — networked and data-heavy software, without sacrificing the verified loop. The spine was one decision: effects are explicit capability values threaded through signatures, so a function's type says it touches the network and the loop verifies against a deterministic mock; real I/O happens only under ptc run, at the typed host boundary. The pure additive stdlib (json, time, patterns) shipped first; then the capability model — Clock, Http, Fs, and the retirement of the last ambient file I/O — and finally the proof: pulse, a networked static-site generator verified with no network at all. The invariant held: the human never sees code the compiler hasn't proved, a networked program included.

  1. Sprint 75jsonshipped

    Phase M11 (capabilities) opens. A Json enum you match on — JSON is dynamically shaped and POLYTONE is statically typed, so a parsed document is a value, never a dynamic any, and a missing key stays Map.get -> None. parse is a recursive-descent parser that returns teaching errors on malformed input; to_text serializes back to canonical JSON, round-tripping byte-for-byte with Int and Float kept distinct. Written in POLYTONE, self-tested, embedded in the browser runtime, additive under §27.

  2. Sprint 76time + mathsshipped

    Two pure modules. time gives an Instant (seconds since the Unix epoch) and a signed Duration: construct from validated UTC calendar fields (of_civil teaches on February 30th rather than lying), break down with to_civil, add and subtract durations, compare, format as ISO 8601 — Hinnant's exact integer calendar math, which assumes the truncating division POLYTONE already has. Reading the clock stays absent on purpose — now is the Sprint-78 capability. maths expands the Float surface beyond the four builtins: pi/tau/e, Float min/max/clamp/sign, integer-exponent pow, hypot, tan, degree/radian conversion, lerp, decimal rounding. Self-tested, embedded in the browser runtime, additive under §27.

  3. Sprint 77patternsshipped

    Two pure Text matchers, and the close of M11's pure-stdlib block. matches_glob is the shell glob — * (any run), ? (one char), [a-z]/[!neg] classes, else literal. matches is a regex-lite subset matched against the whole text (anchored both ends — the right primitive for is-this-valid): literals, ., [classes], the shorthands \d \w \s (and negated), and the quantifiers * + ?, backtracking. A malformed pattern is a teaching error, never a silent mismatch. Written in POLYTONE, self-tested, embedded in the browser runtime, additive under §27.

  4. Sprint 78The capability model + clockshipped

    Block B opens [LLM-first decision]: effects are opaque capability values threaded through signatures (spec §29). Clock is the first — a type you receive, never construct. main may declare capability parameters and the runtime injects the real clock under ptc run; clock.now() reads it (a time.Instant); fixed_clock(instant) builds a deterministic mock. ptc test never calls main, so tests are clock-free by construction — the verified loop stays deterministic, real time crosses only at ptc run, through the typed host, the same boundary read_file/env already cross. The whole vertical slice landed on one capability: typeck, PTIR, the VM and its host, the compiled wasm runtime — the differential gate holds both backends to one clock semantics. Http and native Fs follow the same shape.

  5. Sprint 79http over the capabilityshipped

    The second capability — Clock's shape on a wider effect. The pure http stdlib module carries the values (Method/Status/Request/Response and builders like get/post, is_success); the Http capability carries the one effect, net.send(request) -> Result[Response, Text]. Received via main(net: Http) — a real request under ptc run — or mock_http(routes), a Map[Text, Response] keyed "{METHOD} {url}" that makes network code deterministic, the fixed_clock analogue. The loop verifies against the mock; a real request happens only at ptc run, via the system curl (zero new deps — the toolchain already shells to curl for ptc vendor), never in the browser sandbox or a differential fixture. The main-capability gate and injection point were generalized to a capability set, so Fs slots in mechanically. A networked program is now writable and verifiable.

  6. Sprint 80The filesystem is a capabilityshipped

    The third capability, and the first retirement. Clock and Http were additive; Fs closes a hole — the ungated read_file/write_file builtins were the one ambient effect contradicting the promise that main's signature is the whole effect footprint. File I/O is now disk.read(path) -> Result[Bytes, Text] and disk.write(path, bytes) -> Result[Int, Text] on an Fs received via main(disk: Fs) — host files under ptc run, the sandbox's in-memory VFS in the browser (seed-and-read-back previews unchanged) — or built with mock_fs(files), a Map[Text, Bytes]: reads answer purely from the map, writes report the byte count without persisting. The old builtins retire with a teaching error naming the capability form, so a generator reaching for the old shape is corrected at compile time, not broken silently. The whole surface migrated — nine examples, playground samples, all five media-tool generators, the language card — codec v4, differential 25/25. env stays the one ambient read (deferred until it earns its own capability); args() stays ungated by design: startup input, not an effect.

  7. Sprint 81The proofshipped

    The end-to-end program M11 was built toward, and the close of the phase. examples/pulse.pt is a data-fetching static-site generator: main(wall: Clock, net: Http, disk: Fs) names the whole effect footprint in one line; the pipeline fetches a JSON feed over Http, decodes it with json into typed records (a malformed feed is a typed error naming the field), validates every link with patterns.matches, stamps the build via Clock + time.to_iso, and writes one HTML page through Fs. Five test blocks run the same pipeline on mock_http/mock_fs/fixed_clock — ptc test verifies a networked program with no network, no disk, and no wall clock, down to the exact byte count of the written page — and a ptc integration test CI-gates it. The real path holds too: under ptc run the same program fetched from a live server, stamped real time, wrote a real file. The missing capabilities_fs fixture landed, stale site copy caught up. The capability model earns its keep in one runnable artifact: the human never sees code the compiler has not proved, a networked program included.

82–88Phase M12 — The fine-tuning pass7/7complete

Eleven phases shipped fast; M12 builds nothing new — it re-audits every layer bottom-up, owner-directed: supervise and fine-tune everything that exists before planning what comes next. Eight review rounds (compiler+VM, the capability boundary, PTIR/codec, stdlib/formats, toolchain surfaces, web platform, IDE+launch infra, tests/CI/record), each ending in a numbered findings list the owner confirms — the confirmed lists are the sprint backlogs, the audit record lives in M12-REVIEW.md. M9 proved the protocol; M12 repeats it three phases later.

  1. Sprint 82Compiler + VM: the findings passshipped

    M12 opens. The confirmed R1+R2 backlogs — compiler frontend, VM core, and the capability boundary. The S1s were fixed ahead of the sprint (owner-directed): generic-shadow, nested exhaustiveness by union coverage, the Float→Int boundary, qualified-generic trailing comma, nested-interpolation spans, the capability legibility invariant (opaque + placement-restricted), the codec allocation-bomb, curl argument injection. This sprint clears the rest: a builtin/capability name can't be a function name, async fn -> Task[T] teaches, bare mocks teach 'call it', the lexer rejects trailing '_' and infinite floats, the fuel doc matches, parse_request fails fast, the curl status surfaces a malformed code, and temp files get O_EXCL against a symlink race. It also folds in the pulled-forward remediation and the two early-access apps (Viewer/Player + Converter), and adds preflight + a pre-push hook so a broken gate can't reach develop (the Sprint-81 root cause).

  2. Sprint 83PTIR/codec + stdlib/formats: the findings passshipped

    The confirmed R3+R4 backlogs. json.parse follows the JSON number grammar exactly now — leading zeros (01/007/-01) and a bare trailing decimal point (1.) are teaching errors, an exponent needs a digit; the lax forms broke round-trip fidelity (F4.1). The codec encoder asserts code.len() == spans.len() (F3.3), opcodes 62/63 are reserved (F3.7), and the codec tests gained float bit-pattern round-trips (NaN/±Inf/−0.0 via to_bits), the i64 boundaries, and a multi-byte-UTF-8 string (F3.4/F3.5). The rest of R4 (time, maths, patterns, the five format codecs) was already clean; the differential holds 26/26.

  3. Sprint 84Toolchain (CLI + MCP): the findings passshipped

    The confirmed round-5 backlog — ptc and MCP; the CLI↔MCP symmetry re-audited. Usage help now goes to stderr with a non-zero exit on an unknown command instead of polluting stdout, print_usage and the error path sharing one usage_text() source (F5.7). CLI↔MCP parity for malformed input: ptc check rejects a second file, the MCP context tool rejects a 0 line/column (1-based), and the MCP run tool rejects a non-string args entry instead of silently dropping it (F5.10). And CLI integration coverage for the failing/empty branches — run on a missing file / runtime error / with args, test on a red block and on no tests, fmt --check on a non-canonical file, the doc/build missing-argument paths — each pinning exit code and message so a regression can't ship green (F5.9).

  4. Sprint 85Web platform: the findings passshipped

    The confirmed round-6 backlog. The roadmap became this collapsible phase → sprint tree (owner request). Writing the crafted-input HTML-safety tests (F6.3) surfaced a stored XSS the name-field fix had missed: the video editor rendered an imported .ptv's ops raw into a textarea and the timeline dropdowns rendered names raw — both now escaped (F6.7). The roadmap SEO description went evergreen (it read 'phases M1–M8', three phases stale, feeding meta/OG/JSON-LD; F6.2), and currentRoute() no longer throws on a malformed %-path at boot (F6.4). Web-only — no wasm rebuild.

  5. Sprint 86IDE + launch infra: the findings passshipped

    The confirmed round-7 backlog. The launch infrastructure and license verification were clean; the findings are in POLYTONEide's verified loop. isSafeProjectPath rejects percent-encoded traversal (..%2fevil.pt → ../evil.pt; F7.3). The loop's path= capture broadened so a backslash/Unicode name routes into the path-boundary teaching error instead of vanishing (F7.4). License keys gained an optional expiry (owner decision): fail-closed after the signature verifies, offline against the local clock, absent ⇒ perpetual (F7.5). F7.1/F7.2 (session-replay path-escape + malformed-session crash) were fixed in-round. IDE-only — no wasm rebuild for logic.

  6. Sprint 87Tests/CI/record: the findings passshipped

    The confirmed round-8 backlog. Round 8 found the CI content comprehensive (16 gates, every layer) and the record consistent — the findings are depth and process, not a missing gate. The tracked coverage holes are all closed: nested-interpolation spans (previously only structure was tested), VM-internal fuel exhaustion (previously only exercised downstream in web-rt), and non-BMP LSP diagnostic columns as UTF-16 not scalars (an ASCII-vs-🎼 differential). F8.1 (the Sprint-81 gate-incident root cause: CI is detective, no pre-push control) and F8.2 were fixed in sprint 82 — preflight + the opt-in pre-push hook — and re-verified. Tests only — no wasm rebuild for logic.

  7. Sprint 88The true record, againshipped

    Consolidation close — Phase M12 complete. Fifty-six findings across eight rounds, every one fixed or honestly recorded in M12-REVIEW.md (the backlog of record). Sprint 88 cleared the last polish S4 (the expression-position parse error teaches 'the line ended — a value is missing' instead of naming a token kind), documented the doc-comment blank-line tolerance and the VM Set cost note, and recorded three deferred-beyond-M12 items — VM char-cursor + Set performance, the pattern field-name error span (an AST change), and a cosmetic value-cycle span — each with a disposition and a forward pointer. The next phase is not yet planned.

89–96Phase M13 — Batteries8/8complete

M12 hardened the compiler; M13 grows what an LLM can reliably generate a working program for. Generation hits a wall the moment a task needs a common battery — no random, no csv/base64/hex/url/uuid, patterns is regex-lite, no generic sets/maps modules, and the language has no bitwise operators, so clean hashing/encoding is impossible. M13 is the M11 additive-stdlib pattern at scale, plus one primitive (bitwise ops) that unblocks a whole class of it, plus two new capabilities (random, env). A deliberate dependency chain: sharpen the engine (the deferred M12 debt) → add the primitive → build the batteries → prove it. LLM-first calls up front: patterns stays a subset (not full PCRE), yaml is out, and random/env are capabilities so non-determinism stays visible in main's signature.

  1. Sprint 89Sharpening — the deferred M12 debtshipped

    M13 opens. Pattern field errors now point at the offending field name, not the subpattern (F1.10, an AST name-span through parser → typeck → PTIR). for ch in text: is O(n) not O(n²) via a MaterializeText instruction that snapshots the char list once (F1.7 char-cursor half, codec v5) — a text_scan benchmark exercises it. The value-cycle error never lands at column zero (F1.12a). The hashed Set/Map backing (F1.7's other half) stays a recorded follow-up: keys aren't restricted to hashable primitives, so an order-independent hash mirroring equality across the shared runtime is a dedicated change — it stays fuel-bounded and correct today.

  2. Sprint 90Bitwise operators — the language primitiveshipped

    &, |, ^, <<, >>, and unary ~ on Int through the whole pipeline — lexer, parser precedence, typeck (Int-only, teaching errors on Float), PTIR (opcodes 82–87, codec v6), VM (shift out of 0..=63 is a runtime error), fmt, and spec §4.3. Precedence is LLM-first: bitwise binds tighter than comparison and looser than arithmetic (Rust/Python), so flags & MASK == MASK is (flags & MASK) == MASK — no C footgun. The missing primitive under hashing, encoding, checksums, and flags. A bitwise differential case keeps VM ≡ compiled WASM.

  3. Sprint 91Generic collectionsshipped

    Pure, generic, self-tested modules filling the gaps around the builtin collections. Enabler: List.to_set()/Set.to_list() builtins — set construction from any list, including an empty one. sets (union/intersection/difference/symmetric_difference/is_subset/is_superset/is_disjoint — total). maps (get_or/merge/map_values/from_lists/invert/filter, via parallel keys()/values()). lists combinators (any/all/find/unique/flatten/chunk/min_by/max_by/sort_by [stable generic sort]/group_by). Embedded in the browser runtime + stdlib self-test gate; a conversions differential case. Recorded gap: tuples can't yet be decomposed, so pair-producing helpers (zip/enumerate) are write-only — tuple decomposition is the next enabler.

  4. Sprint 92The Env capability — the last ambient effect retiredshipped

    env promoted from the last ambient read to an Env capability: fn main(sys: Env) receives it, sys.var(name) -> Option[Text] reads a variable (None when unset), mock_env(vars) builds a deterministic one. The ambient env(name) builtin is retired with a teaching error naming the capability form — exactly as Fs retired read_file/write_file. main's signature is now the whole effect footprint — the capability model's promise, complete. The method is sys.var (not get, which would collide with Map.get at lowering). PTIR EnvVar removed, RealEnv/MockEnv/EnvGet added, codec v6→v7; real + mock differential cases. random is deferred to its own sprint: a stateful RNG needs mutating methods that also return a value — a method-system extension not to be rushed alongside a capability.

  5. Sprint 93Encoding — base64, hex, urlshipped

    Pure, self-tested stdlib modules, clean now that the bitwise operators exist. base64 (encode/decode over Bytes, RFC 4648, verified against the RFC test vectors), hex (decode — the inverse of the builtin Bytes.to_hex, case-insensitive), and url (RFC 3986 percent en/decode — unreserved chars pass, every other UTF-8 byte becomes %XX, round-trips Unicode). Embedded in the browser runtime + stdlib self-test gate; they build only on primitives the differential already pins. Richer patterns (alternation/groups/{n,m}) is deferred — a regex-engine change with backtracking correctness deserving its own focus, not a rushed addition alongside three encoding modules.

  6. Sprint 94Data & hashing — csv, sha256, hmac, crc32shipped

    Pure-POLYTONE crypto on the bitwise ops. crypto.pt: sha256/sha256_hex (FIPS 180-4), hmac_sha256 (RFC 2104), crc32 (IEEE 802.3) — 32-bit arithmetic masked with & 0xffffffff, round-constant tables clean because hex literals already exist. Verified against the FIPS SHA-256 vectors, RFC 4231 HMAC case 2, and the CRC-32 check value 0xcbf43926. csv.pt: RFC 4180 parse/serialize — quoted fields (comma/quote/newline, embedded quotes doubled, quoted newlines), LF + CRLF, round-trip. Both embedded (22 modules) + stdlib self-test gate. toml deferred (a fiddly subset) and uuid deferred (v4 needs random, itself pending a mutating-method extension; v5 needs SHA-1).

  7. Sprint 95The Rng capability — randomness, made visibleshipped

    The deferred random battery. Rng is the fifth capability and the first stateful one: fn main(dice: Rng) receives it, rng.int(lo, hi) (uniform, inclusive; lo > hi is a runtime error) and rng.float() ([0, 1)) draw from it, fixed_rng(seed) builds a deterministic one, and the real generator is seeded from host entropy only at ptc run. The engine is SplitMix64 — not cryptographic; use crypto for that. A draw advances the generator, so it needs the new MethodSig::MutatingReturning (mutates the receiver AND returns a value) and a mut receiver: mut gen = dice; gen.int(1, 6). PTIR RealRng/FixedRng/RngInt/RngFloat, codec v7→v8. The effect set is now Clock/Http/Fs/Env/Rng — every non-determinism visible in main's signature.

  8. Sprint 96The proof + the true recordshipped

    Closes M13. examples/digest.pt — a content-addressed manifest builder whose main(disk: Fs, dice: Rng) is the whole effect footprint: read a .csv via Fs, dedupe through a Set and order with the generic collections, sha256 each row, base64-encode the payload, stamp the batch with Rng, emit a json manifest via Fs — every M13 battery in one pipeline. ptc test runs it on mock_fs/fixed_rng (no disk, no entropy → deterministic bytes out), CI-gated in proof.rs beside examples/pulse.pt. The true record: every M13 item fixed or recorded; the deferred tuple-decomposition, richer patterns, toml, and Rng-unblocked uuid v4 seed the next phase.

97–101Phase M14 — Supervised fine-tuning5/5complete

The M13 hardening audit — owner-directed (the same 'fine-tune everything that exists' directive that opened M12), before any new feature phase. M14 repeats M12's protocol on the surface M13 added: one round per area, findings F<round>.<n> with severities S1–S4 and target sprints, the owner confirming each round's list. Scope is M13-primary plus a regression sweep; the record is M14-REVIEW.md. The review confirmed ~22 findings across eight rounds — one S1 (a nested-Rng-draw miscompile) fixed ahead, R5 (toolchain) clean. The fix-sprints: 97 the compiler backlog, 98 the stdlib, 99 the web/docs (regenerating the stale API reference + adding a freshness gate), 100 the IDE language card + differential coverage, 101 the true record.

  1. Sprint 97Fine-tuning: compiler backlog (M14 opens)shipped

    Phase M14 (Supervised fine-tuning) opens — an audit of the M13 surface, the way M12 audited M11. The review confirmed ~22 findings across eight rounds; this sprint clears the R1/R2/R3 compiler backlog. F2.1 (S1, fixed ahead): a Rng draw nested in another draw's argument — g.int(g.int(0, 5), 10) — lost an advance and correlated the two draws, because the MutatingReturning lowering snapshotted the receiver before evaluating the arguments; now the arguments lower first and the receiver last, so a nested draw matches the sequential desugaring, and since exec_simple is shared the VM/web-rt/wasm-rt/compiled-WASM are all corrected with no codec change. F2.2: the Rng lowering asserts its mut-local invariant instead of falling through to a VM panic. F2.3: spec §29 documents the rng.int modulo bias and nested-draw semantics. F3.1: the codec 'every instruction' round-trip corpus now includes opcodes 81–87. F1.1 was verified a non-defect (the variant-pattern name-span already matches records). R5 (toolchain) swept clean.

  2. Sprint 98Fine-tuning: stdlib backlog (R4)shipped

    The R4 stdlib backlog cleared. base64 decode now rejects malformed padding — '=' is valid only as a suffix of the final group (F4.1) — and csv parse rejects content after a closing quote (F4.5, malformed per RFC 4180). The base64 non-canonical-bits leniency (F4.2), the csv empty-row/single-empty-field ambiguity (F4.6), the O(n²) cost of lists.unique/sort_by (F4.8), and digest's 'checks paths are distinct, not deduplicates' wording (F4.10) are documented. Coverage the review found missing is closed: url lowercase escapes + the non-UTF-8 error path (F4.3), the crypto over-long-key HMAC branch (RFC 4231 case 6) + a multi-byte crc32 vector (F4.4), sets/maps empty-operand cases + an explicit keys()/values() parallel-order invariant test (F4.7), and digest's non-UTF-8 + malformed-CSV error paths (F4.9).

  3. Sprint 99Fine-tuning: web/docs backlog (R6)shipped

    The R6 web/docs backlog cleared. The API reference (api.ts) is regenerated from the shipping stdlib — 22 modules, 161 items, with the M13 modules (sets, maps, base64, hex, url, crypto, csv) and the new lists combinators no longer missing (F6.1). A vitest consistency gate now fails if any stdlib/*.pt module is undocumented, closing the root cause that let the drift land behind green CI (F6.3). Spec §19's module list and its 'cannot drift' claim are reconciled with reality (F6.2), the generator's banner path is fixed (F6.4), and the guide's standard-library section teaches the grown stdlib with a crypto/base64 example (F6.5).

  4. Sprint 100Fine-tuning: language card + differential (R7/R8)shipped

    The R7/R8 backlog cleared. The frozen IDE language card (CARD_VERSION 3→4) is brought up to the M13 surface: it had gone stale since M11, still listing the retired env() builtin, and now teaches the bitwise operators (& | ^ << >> ~), the full stdlib (sets, maps, base64, hex, url, crypto, csv), and the Env/Rng capabilities (sys.var, a mut binding for a random draw) — the effect footprint is the full Clock/Http/Fs/Env/Rng (F7.1). A self-contained tests/fixtures/bitwise_hashing.pt (a sha256 sigma, base64 six-bit packing, a crc32 step) gives the compiled backend differential coverage on the 32-bit rotate/mask/packing/crc patterns the import-using crypto/base64 modules lean on — the single-file harness could never reach them through an import. Differential now 30/30 (F8.1).

  5. Sprint 101Fine-tuning: the true record (M14 closes)shipped

    Closes M14. The M14-REVIEW.md disposition & close: all 22 findings across eight rounds fixed or recorded — behaviour fixes (the nested-Rng-draw miscompile, base64 padding, csv after-quote, the Rng lowering invariant), the docs and coverage the review found missing, and two reported findings that shrank to verified non-defects on adversarial verification (F1.1, F3.1's premise). R5 (toolchain) swept clean; no deferred findings. The differential grew 28→30 over the phase. Phase M14 complete — the M13 surface hardened and the record honest. The next phase is unplanned; its seed is the M13 feature carry-forward (tuple decomposition, richer patterns, toml, uuid v4).

102–107Phase M15 — Tuple decomposition6/6complete

Tuples are the one structural type POLYTONE can build but not take apart: (a, b) constructs, but nothing reads an element back, so lists.zip is write-only and maps reads every entry through parallel keys()/values() loops. M15 adds decomposition via patterns (owner-chosen — let (a, b) =, case (a, b):, for (k, v) in; no positional .0/.1), then cashes in the stdlib payoff (enumerate/zip_with/unzip, Map.entries, simpler maps), then rides the two M13-deferred items now unblocked: uuid v4 on the Rng capability and a toml subset. Language ergonomics before the stdlib that needs it. LLM-first calls: patterns only (named binders, not magic indices), Map.entries over an arity-sensitive iterator, toml a documented subset, richer patterns still deferred.

  1. Sprint 102Tuple patternsshipped

    Opens M15. Tuples can finally be taken apart — the one structural type POLYTONE could build but not decompose. A new PatternKind::Tuple and StmtKind::LetPattern, plus a TupleGet opcode (byte 95, codec v8→v9 — the phase's only encoding change) that reads an element positionally, give the let (a, b) = t and case (a, b): forms (nested and refutable, patterns only — no positional .0/.1, an LLM-first call for named binders over magic indices). The lowering mirrors the record-pattern path machinery (one new PathStep), and the token-based formatter round-trips the syntax for free. A differential fixture (now 31) keeps the compiled backend in step; lists.zip is no longer write-only.

  2. Sprint 103for (k, v) destructuring + Map.entries()shipped

    The for-loop binder became a pattern, so for (k, v) in pairs: destructures each element (a bare name keeps a no-copy fast path). A new Map.entries() -> List[Tuple[K, V]] builtin makes pair iteration idiomatic — for x in map stays keys (backward-compatible), and for (k, v) in m.entries(): is the explicit pair form. let and for binders are now required irrefutable: a literal there (like let (0, x) = …) was silently ignored and is now a teaching error pointing at match — which also closed a gap in sprint 102. No codec change.

  3. Sprint 104The collection payoffshipped

    Decomposition cashed in. lists gained enumerate (pairs each element with its index), zip_with (combines two lists element-wise), and unzip (splits List[Tuple[A, B]] back into Tuple[List[A], List[B]], the inverse of zip) — unzip is only writable now because it both destructures each pair and returns a tuple. maps' merge/map_values/invert/filter were rewritten to for (k, v) in m.entries():, retiring the parallel keys()/values() scaffolding the module carried through all of M13 (behaviour identical, roughly half the code), and the invariant test that defended it became an entries() test. The API reference is regenerated (22 modules, 164 items). No codec or compiler change.

  4. Sprint 105uuid v4 (on Rng)shipped

    The first M13 ride-along lands. stdlib/uuid.pt: uuid.v4(gen: Rng) -> Text draws sixteen random bytes, sets the version nibble to 4 and the variant bits to 10 per RFC 4122, and renders the canonical 8-4-4-4-12 lowercase-hex form. Deferred in M13 for want of randomness, now unblocked by the Rng capability — because randomness is a capability, a program that mints UUIDs says so in its signature, and fixed_rng(seed) makes the output reproducible (the self-tests pin the shape, version, and variant deterministically). Pure POLYTONE on the bitwise operators; 23 embedded stdlib modules, 165 API-reference items.

  5. Sprint 106toml subsetshipped

    The second M13 ride-along. stdlib/toml.pt parses and serializes a documented subset — key = value pairs, [table] section headers, # comments, and four value kinds (double-quoted strings with escapes, integers, booleans, single-line arrays) — and round-trips. Floats, dates, nested/dotted tables, arrays of tables, inline tables, and multi-line strings are out (documented), an LLM-first call for reliability over completeness, like patterns and csv. The value model is a Value enum and a Table record whose ordered pairs are a List[Tuple[Text, Value]] walked with for (k, v) in table.pairs: — M15's tuple decomposition earning its keep in the stdlib. 24 embedded modules, 169 API-reference items.

  6. Sprint 107The proof + the true record (M15 closes)shipped

    Closes M15. examples/roster.pt is a tournament-roster builder that exercises tuple decomposition end to end: it zips parallel name/score lists, enumerates them for seeding, destructures each pair nested right in the for binder (for (i, (name, score)) in …), groups the players by tier, walks the groups with for (t, group) in grouped.entries():, stamps the batch with a uuid from Rng, and renders the whole thing through toml (round-tripped back in the tests). main(dice: Rng) is the effect footprint; fixed_rng pins the output, so the tests are deterministic, and it's CI-gated in proof.rs beside pulse and digest. Phase M15 complete: tuples went from build-only to fully decomposable (patterns only, no .0/.1), the collection stdlib was rewritten to use it, and the two M13 deferrals it unblocked (uuid, toml) shipped — the stdlib grew from 22 to 24 modules. The next phase is unplanned; the recorded carry-forward is richer patterns (alternation/groups/{n,m}, a regex-engine change).

108–110Phase M16 — Richer patterns3/3complete

The last item deferred out of M13/M15: a real regex engine for patterns, POLYTONE's regex module written in POLYTONE. Today it is a flat backtracking matcher with literals, ., classes, \d\w\s, and * + ? — no alternation, groups, or {n,m}. M16 replaces the flat token list with a recursive AST and adds alternation |, groups (...), the full quantifier set, and capture extraction, then proves it. It stays a documented subset (no backreferences, lookaround, or lazy quantifiers). The engine is a deduped position-set simulation — a Thompson NFA written as a recursive tree-walk — so it is polynomial and ReDoS-safe without an explicit NFA graph: a pathological pattern can never blow the VM fuel on the matches path. Pure stdlib, no compiler or codec change.

  1. Sprint 108The regex engineshipped

    Opens M16. patterns' flat backtracking token list is replaced by a recursive Node AST (Concat/Alt/Repeat{min,max}/Lit/Any/Class) and a recursive-descent parser with real precedence, adding alternation |, groups (...) (non-capturing, transparent), and the full quantifier set * + ? {n} {n,} {n,m}, any of which may nest. The matcher is a deduped position-set simulation — a Thompson NFA written as a recursive tree-walk (match_here returns the set of reachable end-positions, deduped with Set[Int]): polynomial and ReDoS-safe without an explicit NFA graph, so a pathological pattern like (a|a)*b returns at once instead of blowing the VM fuel (a test pins it). All current matches/matches_glob behaviour, the examples/pulse.pt URL regex, and the four error-message contracts stay green. Pure stdlib — no compiler or codec change.

  2. Sprint 109Capture extractionshipped

    captures(pattern, text) -> Result[Option[List[Text]], Text] returns the whole match plus each group's captured substring, in group order, when the pattern matches (anchored) — captures("(\d+)-(\d+)", "12-345") gives Some(["12-345", "12", "345"]) — and Ok(None) when it does not. Groups gained a capture index (a re-added Node.Group numbered by a post-parse pass); matches still treats them transparently. Because capture needs per-path group boundaries (position-set dedup would merge distinct captures), this path uses a capturing backtracking walk that threads the spans — value semantics discard a failed branch's captures for free, and a progress-required guard keeps repeats terminating. Groups without capture were half a feature; this is what makes richer patterns useful for pulling values out of text.

  3. Sprint 110The proof + the true record (M16 closes)shipped

    Closes M16. examples/logparse.pt is a log-line parser whose one regex — (\d{4})-(\d{2})-(\d{2}) (INFO|WARN|ERROR) (\w+): (.*) — exercises every feature the phase added: bounded quantifiers, a group with alternation, and six capturing groups. It drives both patterns.matches (validate a line) and patterns.captures (pull the fields into a record), then filters and extracts across many lines — CI-gated in proof.rs beside pulse, digest, and roster. Phase M16 complete: patterns went from a flat token matcher to a real regex engine (alternation, groups, {n,m}, capture), all pure stdlib with no compiler or codec change the whole phase, on a deduped position-set matcher that stays polynomial and ReDoS-safe. This clears the last item deferred out of M13/M15; the next phase is unplanned.

111–115Phase M17 — Supervised fine-tuning: the full-codebase audit5/5complete

The third fine-tuning pass, owner-directed ("Supervised fine tuning of absolut all") — the widest scope yet: M15 (tuple decomposition) and M16 (the patterns regex rewrite + captures), neither reviewed since it shipped, at primary depth, plus a genuine regression re-sweep of the M12/M14-covered surface. M17 reuses M12/M14's protocol — one round per area, findings F<round>.<n> with severities S1–S4 and target sprints, the owner confirming each round's list; the record is M17-REVIEW.md. Sprint 111 opens it with the review record and the two confirmed S1s, both in patterns.captures and both fixed ahead by unifying captures onto the deduped position-set engine matches already used. The remaining rounds' S2/S3/S4 findings are targeted at the fix-sprints that follow.

  1. Sprint 111The review record + the S1 remediation (M17 opens)shipped

    Opens M17, the third supervised fine-tuning pass (owner-directed: "Supervised fine tuning of absolut all"), the widest scope yet — M15 (tuple decomposition) and M16 (the patterns rewrite) at primary depth plus a regression re-sweep of the M12/M14 surface. It carries the review record (M17-REVIEW.md) and the two confirmed S1s, both fixed ahead. Both were in patterns.captures, which used a separate backtracking walk never reconciled with matches' deduped position-set engine. F4.1: on a nullable repeat ((a?)+ on "") matches returned true but captures returned None — they disagreed on whether the text matched. F4.2: that walk catastrophically backtracked — captures("(a*)*c", 24 a's + b) ran over 20 s while matches was instant. Both fixed by unifying captures onto the same deduped position→captures simulation: position dedup kills the ReDoS and a shared zero-width fixpoint makes the engines agree by construction. Two regression tests; pure stdlib, no codec change, differential holds 31/31.

  2. Sprint 112The R4 stdlib backlogshipped

    Clears M17's Round 4 (stdlib) backlog. patterns: an inverted bound a{2,1} (min greater than max) is now a parse-time teaching error rather than a pattern that silently matches nothing (F4.3 — parse_brace returns a Result so the frontend distinguishes a stray { literal from a malformed bound); and doubled-quantifier rejection is symmetric — a*{2}, a{2}{3}, and a{2}+ all teach like a** does (F4.4). toml: trailing # comments are stripped, respecting a # inside a quoted string so a URL keeps its fragment (F4.6), and the header documents that quoted/dotted keys are out and that a bare key belongs to the most recent [header] (F4.8). Coverage lands for patterns parser edges (empty alternation branches, a stray close-paren, (*)), toml value and scoping edges (empty string / empty array / trailing comma / escapes), and lists enumerate/unzip/zip_with on the empty list (F4.5/F4.7/F4.9). Pure stdlib, no codec change.

  3. Sprint 113The R1/R2 compiler backlogshipped

    Clears M17's Round 1/2 (compiler frontend + VM/lowering) backlog. A refutable let/for binder error now points at the culprit sub-pattern and names it — let (a, 0) = pair underlines the 0 and says "…but this is a literal" (a nested Some(b) says "…a constructor") — via a new first_refutable walk whose .is_none() is exactly the old irrefutability gate, so the check is unchanged and only the diagnostic is sharpened (F1.2). The VM's TupleGet out-of-range arm is now a named unreachable! reporting the index and the tuple's arity, matching its non-tuple arm, rather than a generic .expect (F2.1). And tuple-match exhaustiveness is pinned by tests (F1.1): the product of a Tuple[Enum, Enum] is not decomposed, so enumerating all combinations is still non-exhaustive without a catch-all (case _: or an irrefutable case (a, b):). No codec or instruction change.

  4. Sprint 114The R6/R7 web + IDE backlogshipped

    Clears M17's Round 6/7 (web/docs + IDE) backlog. Spec §19 now lists toml and uuid and the M15 lists/maps additions (F6.1; §13.2 already documented tuple patterns). The guide gained a "Tuples & destructuring" section — let (a, b), case (a, b):, the nested for (rank, (name, score)) in … binder, the no-.0/.1 rule, and Map.entries() — plus a patterns.captures example and toml/uuid in the stdlib tour (F6.2). The IDE language card bumped CARD_VERSION 4 to 5 to teach tuple decomposition, Map.entries, and the richer patterns/toml/uuid — it had predated the whole M15/M16 surface (F7.1). A stale "17 fixtures" guide count went evergreen (F6.4), and the api.ts freshness gate now checks per-item, not just per-module (F6.3). Docs, tests, and the frozen card only — no stdlib, codec, or compiler change.

  5. Sprint 115The R8 fixture + the true record (M17 closes)shipped

    Closes M17. tests/fixtures/pattern_walk.pt (F8.1) — a self-contained mini-regex matcher (a recursive Node enum walked by a match_here that threads deduped position sets via .to_set().to_list() with a Star fixpoint, plus tuple-for-destructuring and Map.entries()) — gives the compiled backend differential coverage on the import-using M15/M16 shape the single-file harness couldn't reach through an import (differential 31 → 32, CI-gated). The M17-REVIEW.md disposition closes all ~20 findings across eight rounds: R3 (codec) and R5 (toolchain) clean, no verified non-defects, no deferred findings; codec stayed v9 the whole phase. Phase M17 complete — two phases of change (M15 tuple decomposition, M16 the patterns regex rewrite) that had shipped unreviewed are hardened, plus a regression re-sweep of the M12/M14 surface. The load-bearing find was the design seam behind both S1s: captures had grown a second matching engine in M16 never reconciled with matches; unifying them closed a real ReDoS and a real correctness disagreement at once.

116–120Phase M18 — IDE model backends (116–120) — complete5/5complete

Owner-directed. The next phase continues the IDE-integration arc the subscription-OAuth work opened: the workbench learns to talk to many model backends well. IDE product work (the Phase M8 lineage), building on the enumerable PROVIDER_CATALOG, the anthropic-oauth adapter, and the Tauri native bridge. Provisional sprints: 116 a data-driven provider picker rendered from PROVIDER_CATALOG (one entry adds a backend); 117 a first-class local-model backend (Ollama preset, model discovery, health check) past the bare compatible kind; 118 streaming (SSE) completions + retry-with-backoff; 119 a local-CLI bridge (shell out to an installed agent through a Tauri command — the non-API local path, no registration); 120 ship-readiness, a cross-backend token ledger, a backend-matrix proof, and the close. Local-first BYO (no hosted proxy — a request goes only to the chosen backend); the OAuth go-live stays deferred on the Anthropic client registration, not an M18 defect; ChatGPT stays API-key or local.

  1. Sprint 120Ledger + matrix proof + ship-readiness (M18 closes)shipped

    Closes M18 (IDE model backends). A cross-backend token ledger (intents.byModel, pure + tested) aggregates the intent history per model; the ledger line shows the per-model split when more than one model was used, so cost is legible across backends. The backend-matrix proof (a core.test.ts invariant) asserts every ProviderKind appears exactly once in PROVIDER_CATALOG, each has exactly one credential mode matching its auth, and availableProviders hides exactly the desktop-only kinds off the desktop — the backends are coherent by construction. oauthConfigured(client) gates the subscription-login path on a real client id, so it flips on cleanly the moment the Anthropic registration fills DEFAULT_OAUTH_CLIENT. Phase M18 complete: the workbench talks to Anthropic (key or subscription login), OpenAI, a local HTTP runner (Ollama, with discovery), and a local agent CLI through one data-driven catalogue, with streaming + retry — adding a backend is one catalogue entry plus its transport. Carried forward: the OAuth go-live (the external registration) and live streaming progress in the panel. No compiler/stdlib/codec change the whole phase.

  2. Sprint 119A local-CLI agent backendshipped

    A local-cli backend shells out to an agent command the user already has installed and logged in (claude -p, llm, a wrapper script) — the non-API local path with no registration, billing against that tool's own auth. Native run_agent Tauri command (cargo check/clippy clean): it spawns command args…, writes the prompt to stdin on a separate thread so a large prompt can't deadlock against the child's stdout pipe, and returns stdout — or the exit status + stderr on failure; a webview can't spawn a process. local-cli.ts (pure + tested): buildCliPrompt flattens the frozen card + conversation into one stdin prompt, parseCommand splits the binary from its args, runCliComplete runs it through run_agent. A new catalogue entry with auth "command" and a providerFields.command flag shows a command box (still data-driven); ProviderConfig gains an optional command; completeFor routes it (wrapped in the same withRetry). IDE product work — no compiler/stdlib/codec change.

  3. Sprint 118Streaming (SSE) + retry-with-backoffshipped

    The streaming engine and rate-limit resilience, both pure/injected cores tested offline. streaming.ts: deltaFromEvent reads a text delta from either wire shape (Anthropic content_block_delta, OpenAI choices[].delta.content); SSEDecoder is an incremental decoder that buffers an event split across network chunks so a delta is never lost or doubled; streamComplete drives an async chunk source, emits each delta, and returns the accumulated Completion; buildStreamRequest adds stream: true. retry.ts: isRetryableStatus (429 + transient 5xx), isRetryableError (reads the status out of a provider error message), backoffMs (exponential, 8s ceiling), and withRetry with an injected sleep. completeFor now wraps every backend in withRetry, so a 429 or transient 5xx backs off and retries instead of sinking the generation. The streaming transport ships tested-and-ready; threading deltas into the panel as live progress remains a follow-up (not yet wired). IDE product work — no compiler/stdlib/codec change.

  4. Sprint 117A first-class local-model backendshipped

    The local-runner path (Ollama, llama.cpp, LM Studio) becomes first-class: the workbench discovers what's installed and says clearly when the server is down, past the bare compatible kind's free-text model box. localmodels.ts (pure + tested): buildModelsRequest targets the OpenAI-compatible /v1/models (which all three serve), parseModels reads both the OpenAI shape (data[].id) and Ollama's native /api/tags shape (models[].name), and discoverModels returns the installed models or throws an actionable "not reachable at <url> — is it running?" error. The config form gains a "↻ models" button that fills a datalist on the model input (pick from installed models instead of typing) and a status line; the base-URL box defaults to Ollama's localhost:11434. Catalogue-driven via a new providerFields.discover flag. IDE product work — no compiler/stdlib/codec change.

  5. Sprint 116A data-driven provider picker (M18 opens)shipped

    Opens M18 (IDE model backends). The IDE's config UI is rendered from PROVIDER_CATALOG instead of hardcoded options: the <select> is built at mount (one catalogue entry = one backend), and the credential fields follow each provider's auth — a metered backend shows the API-key input, subscription login shows the Login-with-Claude box, a local endpoint shows neither; desktop-only backends (the OAuth loopback) appear only in the Tauri shell. New pure helpers availableProviders(desktop) / providerFields(kind) / providerMeta(kind) make the config form a tested function of the catalogue, so adding a backend no longer means touching the picker markup or its show/hide logic. IDE product work (the Phase M8 lineage) — the compiler, stdlib, and codec are untouched.

121–126Phase M19 — Supervised fine-tuning: the M9 audit6/6complete

Owner-directed. The fourth supervised fine-tuning pass, over the Phase M9 surface (the consolidation pass, sprints 63–70). M9 was itself a review-driven hardening pass, but it is now ten phases old, and much of what it built has never been re-audited: M14 and M17 stayed on the stdlib/compiler surface (M13/M15/M16), while the web, media, shared-runtime, and async/Task surface M9 created has had continuous feature work but no dedicated audit since — that is the emphasis. M19 reuses M12/M14/M17's protocol (one round per area, findings F<round>.<n> with severities S1–S4 and target sprints, the owner confirming each round; record M19-REVIEW.md). The rounds: R1 the shared runtime (polytone-web-shared — the bridge, highlighter, y4m/ppm decoders, the wasm build-input plumbing), R2 the web harness + consistency gates, R3 crate depth (ptir/driver/wasm-rt/ast + the fixture harnesses + MCP/LSP hardening), R4 codegen/differential/freshness, R5 async/Task semantics (§28), R6 the media tools (image/sound/video editors, viewer) + format docs — the largest un-re-audited surface, R7 the POLYTONEide verified loop, R8 spec/docs + tests/CI + the record. Provisional: 121 opens (record + any S1 ahead), 122 R1+R2, 123 R3+R4, 124 R5+R6, 125 R7 (R8 clean), 126 the true record + close.

  1. Sprint 126The true record + Phase M19 closeshipped

    Closes Phase M19 (the supervised fine-tuning audit of the Phase M9 surface, sprints 121–126). A records-only close: the M19-REVIEW.md disposition marks every one of the ~19 findings across eight rounds fixed or recorded — nothing deferred, and (unlike M14/M17) no finding shrank to a non-defect, so the M9 surface held up cleanly and the three confirmed S1s (F1.1 the y4m/ppm decoders, F5.1 the async-fn-returns-capability leak, F6.1 the dropped hidden layer) were real and are fixed. Two rounds were swept clean (R6 media-tool HTML-safety, R8 the record); the R7 M9 verified-loop hardenings were confirmed intact under M18's backends. Codec stayed v9 the whole phase; the differential grew 32 → 34 → 35; stdlib held at 24 modules. Next: Phase M20 (the second full-codebase audit, sprints 127–132) — M1–M8 + M18 primary, M13/M15/M16 regression, by area.

  2. Sprint 125The R7 backlogshipped

    Clears M19's Round 7 (IDE) backlog — the last fix-sprint before the close (Round 8 was swept clean). The IDE's provider dispatch — route a ProviderConfig to its key/oauth/cli runner, then wrap the call in withRetry — lived inside the DOM-heavy intentpanel.ts and could not be imported under node --test; it is extracted to a DOM-free core module, ide/src/core/complete-router.ts, that takes an injected CompletionRunners object, with complete-router.test.ts covering the kind routing, the retry-wrap (a 503 retries then succeeds), a non-retryable error passing through, and retry exhaustion (F7.2). Streaming completions now honour the provider's usage events — Anthropic's message_start/message_delta, OpenAI's final usage chunk — via usageFromEvent and SSEDecoder.usage(), falling back to a ~4-char estimate only per field the provider omitted, so an enabled streamed tier reports real usage instead of silently switching its ledger to estimates (F7.3). And a stale roadmap pointer calling usage-delta threading a 'follow-up (120)' — which sprint 120 never wired — is corrected (F7.1). No codec or compiler change.

  3. Sprint 124The R5/R6 backlogshipped

    Clears M19's Round 5/6 (async/Task + media tools) backlog. tests/fixtures/async_capability.pt covers a Task that captures an Rng (via fixed_rng) through an async fn and defers the draw — pinning, on both the VM and the compiled backend (differential 34 to 35), that the deferral works and that re-running one Task re-draws identically; the async × capability surface the five capability phases added after async shipped, previously uncovered (F5.2/F5.4). Spec §28 now reconciles Task with capabilities (§29): an async fn may capture a capability and defer its effect (so main's effect footprint is an upper bound — a captured effect never run never fires), it may not return a capability, and a Task holding a stateful Rng re-draws identically on every run (F5.3/F5.4). The video editor shows a note that .ptv references but does not embed the soundtrack — export .pt to keep it — instead of losing it silently on reopen (F6.2). And the sound studio flags an unknown song-chain token inline rather than only as a raw codec error at render (F6.4). No codec or compiler change.

  4. Sprint 123The R3/R4 backlogshipped

    Clears M19's Round 3/4 (crate depth + codegen/differential) backlog. The LSP framing layer caps Content-Length at 16 MiB before allocating, so a crafted header can't crash the server with a multi-GB allocation — the oversized-frame guard the doc promised now exists (F3.1). polytone-wasm-rt's pt_alloc returns a null pointer on a length past isize::MAX instead of aborting the module, mirroring polytone-web-rt's buffer_layout guard (F3.2). The compiled-WASM differential walks tests/fixtures recursively like the VM harness, so a subdirectory fixture is no longer silently skipped for bit-exactness — the modules/ leaf modules join the set, differential 32 to 34 (F4.1). bench-compiled.mjs reports a per-program failure row instead of aborting the whole report on one nonzero exit (F4.2), and a dead ptc-version tail after the exit gate is gone (F4.3).

  5. Sprint 122The R1/R2 backlogshipped

    Clears M19's Round 1/2 (shared runtime + web harness/gates) backlog. The wasm freshness gate now derives the two blob crates' path-dependency closure and fails (in --stamp/--check) if INPUTS misses a crate, so a forgotten dependency can't ship a stale-semantics blob behind green CI — the drift the gate exists to prevent (F1.3). The highlighter's number scanner stops at .., so `for i in 0..10:` renders 0, .., 10 instead of one number (the 5.abs() single-dot quirk is preserved) (F1.4). And the per-item api-parity gate fails loudly on any pub <kind> gen-api.mjs does not handle — the lexer has a trait keyword, so a pub trait would otherwise be silently undocumented and ungated (F2.1). Runtime TS + a build-gate change — no compiler/stdlib/codec change.

  6. Sprint 121The review record + the S1 remediation (M19 opens)shipped

    Opens M19 (the M9 audit), the fourth supervised fine-tuning pass, over the Phase M9 surface (the consolidation pass, sprints 63–70) — emphasis on the web/media/shared-runtime/async-Task surface no later pass re-audited. M19-REVIEW.md records eight rounds by area, ~19 findings, three confirmed S1s fixed ahead. F1.1: the shared y4m/ppm decoders threw on a valid-looking zero-dimension/zero-fps header (new ImageData(0,0) → an IndexSizeError the null-only callers can't catch; a runaway 1000/fps player loop) → decodePpm/decodeY4m now return null. F5.1: an async fn returning a capability inferred Task[Cap], leaking a capability into a List via tasks.all past the effect-legibility guard → the typechecker rejects an is_capability return on an async decl. F6.1: the image editor silently dropped hidden layers on .pti export → a hidden layer is preserved as commented-out ops under a // layer: name (hidden) marker (the codec skips it, parsePti restores it), so the save format stops losing user work. Clean verdicts: the five M9 IDE-loop hardenings hold under M18's backends, media-tool HTML-safety is not bypassable, the record is consistent. No codec change; differential 32/32.

127–132Phase M20 — The second full-codebase audit6/6complete

Owner-directed. After M19 audits M9, the audit runs again for the phases that never had a dedicated one — M1–M8 (only swept contemporaneously by M12) and M18 (brand new) — plus a regression re-sweep of M13/M15/M16. The fifth supervised fine-tuning pass; owner-chosen shape: one comprehensive full-codebase audit organized by area (the M12 model, now at the M18 codebase), not a dozen per-phase audits. It de-conflicts with the targeted passes: where M19 owns the M9 consolidation infrastructure and M14/M17 own the M13/M15/M16 stdlib, M20 spends its depth on the original-feature substance of M1–M8 (parser / type system / VM / codec / formats) and the M18 IDE backends, treating the rest as regression. Reuses the M12/M14/M17/M19 protocol (findings F<round>.<n>, severities S1–S4, supervised, record M20-REVIEW.md), and runs after M19 closes. Rounds by area: R1 compiler frontend, R2 VM/PTIR/codec, R3 stdlib, R4 web/docs, R5 media tools/formats, R6 POLYTONEide (M8 + M18 backends at primary depth), R7 toolchain (ptc/MCP/LSP), R8 tests/CI + the record. Provisional: 127 opens, 128 R1+R2, 129 R3, 130 R4+R5, 131 R6+R7, 132 close.

  1. Sprint 132The true record + Phase M20 closeshipped

    Closes Phase M20 (the second full-codebase audit, sprints 127–132). A records-only close: the M20-REVIEW.md disposition marks every one of the 34 findings across eight rounds fixed or recorded — nothing deferred, no finding shrank to a non-defect. The six S1s (the monomorphization non-termination, the parser stack overflow, the capability-inference bypass, and the three stdlib crash/hang paths) were all real and are all fixed with regression tests — the widest S1 count of any audit pass, fitting the widest scope: the M1–M8 original-feature substance had never had a dedicated audit. R2 (VM/PTIR/codec) had no S1/S2 and R8 was swept clean; F2.4/F2.5 are deliberate semantics documented in place, and F7.5 keeps its termination by design with an honest error. The audit's recurring theme — decoder/transport input-hardening — is now uniformly closed: every parser, decoder, and transport in the tree bounds its input and errors loudly. Codec stayed v9 the whole phase; differential 35; stdlib 24 modules; web 98 / ide 124 tests. With M20, every phase through M18 has been audited at least once (M12, M14, M17, M19, M20).

  2. Sprint 131The R6/R7 backlogshipped

    Clears M20's Round 6/7 (POLYTONEide + toolchain) backlog — the last fix-sprint before the close. IDE: F6.1 — the Generate precondition required an API key for every kind but 'compatible', locking out the shipping local-cli and anthropic-oauth backends with 'configure a provider first'; a data-driven configShortfall reads the catalogue's auth mode (api-key kinds need a key, command needs the agent command, oauth/none need no local credential) and names what is actually missing. F6.2 — a routed record persisted one model with every tier's tokens summed, so an escalation showed the cheap model at zero; the record now carries the router's perTier split and byModel attributes each tier's tokens to its own model (grand totals unchanged). F6.3 — buildStreamRequest sends stream_options.include_usage for openai/compatible, so the provider actually emits the usage chunk the M19-F7.3 reader consumes. F6.4 — the SSE decoder accepts CRLF event framing incrementally. Toolchain: F7.1 — the MCP stdio transport read lines unbounded, the exact class the LSP's M19-F3.1 cap closed; a bounded 16-MiB reader drains an oversized line, answers -32700, and keeps the session alive. F7.2 — LSP header lines capped at 64 KiB (F3.1 bounded only the body). F7.3 — one non-UTF-8 byte no longer terminates the MCP session (lossy decode, the recoverable parse-error path). F7.4 — MCP context rejects a position past u32::MAX or a non-integer as a named protocol error, in parity with the CLI's --at. F7.5 — a malformed Content-Length is named instead of misreported as missing. Coverage: ide 124 tests, MCP/LSP stdio suites +3 each (F6.5/F7.6).

  3. Sprint 130The R4/R5 backlogshipped

    Clears M20's Round 4/5 (web/docs + media formats) backlog — docs and web TypeScript only. F4.1: spec §21 still presented the retired env() builtin as live (echoed by §8's prelude list, the §12 WASM notes, and §22's sandbox notes), contradicting §29's Sprint-92 retirement; all four sites now describe the Env capability (sys.var/mock_env) and why args() stays ungated. F4.2: timelineRow, the actual M12-F6.1 stored-XSS culprit, was module-private and unreachable by the HTML-safety gate; it is exported (with applySession as the state seeder) and every row kind is asserted neutralised. F5.1: paintThumb re-implemented the P6 parse without the M19-F1.1 zero-dimension guard; it now routes through the shared decodePpm. F5.2: the studio exported 'song: ' for an empty chain, which its own parsePta rejected after trimming; an empty chain now serializes as a bare 'song:' and parses back. F5.3: parsePtv enforces the documented grammar — a v2 surface (sprite/move/audio:) in a v1 document names the bump, fps carries the 1–30 bound, background must be #rrggbb. F5.4: model3d's correct literal escape is promoted to polytone-web-shared as sourceLiteral (completed with the brace escapes the lexer supports) and used by all four media tools. F5.5: an imported multi-decimal duration (play title 1.25) survives re-serialization instead of rounding. F5.6: parsePti enforces the 1–4096 size bound; parsePta rejects a pattern name the space-split song: chain could never reference. Web 98 tests, shared 16.

  4. Sprint 129The R3 backlogshipped

    Clears M20's Round 3 (stdlib) backlog — pure stdlib, no codec or compiler change. F3.4: images.from_ppm_bytes capped decoded sizes at 1024, but canvas/render produce images up to 4096 per side, so a wider-than-1024 image failed the to_ppm_bytes → from_ppm_bytes round trip and the doc's 'any binary PPM' claim overstated; the bound now matches (1–4096 per side). F3.6: video.to_y4m_bytes divided by frame_size (width·height·3), trapping on a hand-built zero-size Video (unreachable via render, which enforces 1–1024); it now emits the header alone, staying total. F3.5: a dead .ptw sub-clause in the web button-target check is removed — a .ptw path already fails ends_with('.pt'), so the explicit term never fired; behaviour is unchanged. Both wasm blobs rebuilt + re-stamped, differential 35/35.

  5. Sprint 128The R1/R2 backlogshipped

    Clears M20's Round 1/2 (compiler frontend + VM/PTIR/codec) backlog, led by the sixth S1. F1.1: polymorphic recursion (deep[T] calling itself at List[T]) type-checked but expanded forever in the monomorphizer (each instance a distinct, deeper type the worklist never dedups) — ptc check passed, ptc run/build aborted. The pass now bounds monomorphized type-argument depth at MAX_MONO_TYPE_DEPTH = 64 and returns a teaching error naming the non-terminating recursion; monomorphize/monomorphize_program now return a Result. F2.1: the PTIR decoder rejects a structurally invalid blob — zero functions, an out-of-range Call/MakeClosure/test function index, or a jump past the end — instead of decoding cleanly and then panicking in the VM (functions[0]/functions[start]), honoring the loud-error contract. F2.3: fetch_args bounds-checks the wasm32 host args blob so a truncated blob ends the list cleanly rather than aborting the module. F1.4: the InvalidNumber message states the true Int bound (2^63 − 1) and the i64::MIN literal recipe. F2.2: the RngInt cost comment corrected + a full-i64-span draw test. F2.4/F2.5: the empty-Set/Map {} overlap and the NaN set/map-key IEEE-754 edge documented in place. No codec change (v9); both wasm blobs rebuilt + re-stamped, differential 35/35.

  6. Sprint 127The review record + the S1 remediationshipped

    Opens Phase M20 (the second full-codebase audit, sprints 127–132) — the fifth supervised fine-tuning pass, owner-directed to re-run the audit over M1–M8 + M18 at primary depth (the original-feature substance never dedicatedly audited), M13/M15/M16 as regression. M20-REVIEW.md records eight rounds by area with ~34 findings; five of six confirmed S1s are fixed ahead. F1.2: a deeply nested expression overflowed the recursive-descent parser and aborted ptc check / the LSP / the MCP server (all parse untrusted .pt text) — a depth counter bounded at MAX_EXPR_DEPTH = 128 now yields a clean teaching error. F1.3: the capability-placement guard (§29) was bypassable via type inference over container/tuple literals (let xs = [wall] accepted while the annotated List[Clock] form was rejected — the M19 F5.1 hole class) — the four literal-inference arms now reject an inferred capability element. F3.1: web.render trapped on a bare heading line — now length-guarded. F3.2: texts.pad_left/pad_right looped forever on an empty fill — now returns the input. F3.3: mesh.render_view crashed on a builder-made mesh (empty colors) — now a neutral-grey fallback. The sixth S1 (F1.1, a generic fn calling itself at a larger type expands forever in monomorphization) leads sprint 128. R2 (VM/PTIR/codec) and R8 (record) swept with no S1/S2 — the M1/M5 core held. No codec change (v9); both wasm blobs rebuilt + re-stamped, differential 35/35.

133–139Phase M21 — Generation excellence: the leading-LLM-language proof loop7/7complete

Owner-directed: make POLYTONE the leading LLM programming language — and with it the founding dream, the best software language to work more performantly with. After five audit passes the codebase is hardened end to end; M21 turns the founding claim into a measured, improvable number. 'Leading LLM language' is an empirical claim: give a model a task, let it write POLYTONE, and let ptc test judge — deterministically, no human in the loop. The instrument (benchmarks/gen/): a task corpus (prompt, hidden judge appended to the candidate, reference solution), a local BYO-key runner that feeds a failure's real ptc error back for one repair attempt (pass@1 and pass@2e both first-class — the teaching-error loop IS the product), and a CI gate that keeps the corpus from rotting (every reference solution must pass its own hidden tests; CI never calls a model). Provisional sprints: 133 the instrument + a ten-task seed corpus; 134 the corpus grows to ~30 tasks (media formats, capability composition, generics, difficulty tiers); 135 baselines against frontier + local models and the site's honest benchmark page (methodology, per-model pass@1/pass@2e, the full task table); 136 data-driven hardening I (whatever the failures reveal first — teaching-error wording, card gaps, doc gaps); 137 data-driven hardening II (the deeper stdlib/ergonomics cuts); 138 the delta proof (full re-run, before/after per task); 139 proof + the true record + close.

  1. Sprint 139The true record + Phase M21 closeshipped

    Closes Phase M21 (Generation excellence, sprints 133–139) — the phase that turned 'leading LLM programming language' from a claim into standing, measurable infrastructure. 133: the instrument (benchmarks/gen/ — task corpus, the hidden-judge protocol, the BYO-key runner, the corpus-rot CI gate). 134: the corpus tripled to 30 tasks across the full surface with S/M tiers. 135: the public /benchmark/ page + publishing pipeline + drift gate ('this page never fabricates a number'). 136: the live-site incident — the owner's screenshot exposed 44 sprints of silently failed deploys, fixed and made structurally impossible (the deploy smoke as preflight's fourth gate). 137: hardening from the phase's first failure dataset (Text.slice, the qualified-enum teaching error, Card v6). 138: the delta proof (recorded first attempts replayed, CI-gated: fail → pass, nonsense → teaching). Differential 35 → 36; codec v9 unchanged all phase; web 99 / ide 124 tests. Carried forward, deliberately: frontier baseline runs are BYO-key — publishable into benchmarkRuns at any time; every future phase can feed the loop new tasks, failure data, and deltas. A records-only close (no code change).

  2. Sprint 138The delta proof — replaysshipped

    The Sprint-137 hardening becomes a measured delta: the recorded first-attempt candidates from the phase's first failure dataset (the Sprint-133 authoring session, verbatim) are replayed against the current toolchain through the exact judge path (candidate + the task's current hidden tests → ptc test), CI-gated in ptc/tests/gen_replays.rs so the delta can never silently regress. content_tag_attempt1 — failed then (no method 'slice' on Text); passes now, verbatim: fail → pass, the language grew to meet the model. json_pluck_attempt1 — failed then with a mismatch that read like nonsense; still fails (the pattern is wrong) but the error now teaches the qualified form — the repair signal pass@2e depends on. log_scan_attempt1 — still fails (groups[0] is the whole match, a semantics miss no compiler error can prevent), but the judge's assertion diff carries the actual values, and Card v6 teaches the rule. The /benchmark/ page documents the replays (then/now, per candidate) — showing only what has actually run; fresh frontier pass@1/pass@2e runs stay local and BYO-key.

  3. Sprint 137Hardening from the first failure datashipped

    The phase's first generation-failure dataset is the Sprint-133 corpus-authoring session itself — an LLM writing POLYTONE cold, its stumbles documented. From it: Text.slice(from, to) now exists — the method models reach for reflexively (the dataset's first failure forced the non-obvious .to_bytes().slice(…).to_text() → Option dance); character-based, strict [from, to), bounds-checked with the same teaching error as Bytes.slice (LLM-first: the two slice methods agree on semantics). Typeck + the shared VM (both backends), spec §14, the Prelude explorer, the guide, and a differential fixture (text_slice.pt, 35 → 36, Unicode included). The unqualified imported-enum pattern — case Json.Str(…) against a json.Json subject — used to produce 'this pattern matches Json values, but the subject has type json.Json', a mismatch that reads like nonsense; it now teaches the qualified form and names the exact pattern to write. Card v6 teaches the dataset's remaining lessons: captures returns [whole match, group 1, …], imported enums match qualified, for ch in text: walks characters, \{ for literal braces (JSON strings!), and Text.slice. The content_tag reference solution uses the natural slice form — the corpus tracks the language it measures. No codec change (v9).

  4. Sprint 136The live-site incident — the deploy unblockedshipped

    The phase's first data-driven hardening — and the first real-world failure M21 surfaced was the deploy pipeline itself: the owner spotted the live site's changelog frozen at v0.13.91 (2026-07-31). Root cause: the deploy workflow's first step, runtime/web/smoke.mjs, still exercised the ambient env() builtin that Sprint 92 retired for the Env capability — so the smoke step failed on every push for 44 sprints and the All-Inkl site never advanced while the repo moved to 0.21.135. The smoke section now runs the capability form (fn main(sys: Env), sys.var); the full deploy chain (site build, IDE build under /app/, dist verifications, bundle smoke) reproduced green locally. The changelog page's lead, which still described the pre-renumbering 0.SPRINT.0 scheme, now teaches 0.PHASE.SPRINT and the 0.x-beta rule. And the durable fix: the deploy smoke joined scripts/preflight.mjs as the fourth fast gate (~1 s) — a gate that only runs remotely and unobserved is not a gate.

  5. Sprint 135The benchmark page + the publishing pipelineshipped

    The benchmark gets its public face; baseline runs stay local and BYO-key (the owner's move — no keys live in the build environment). The site's /benchmark/ page carries the honest methodology (the ptc test judge, appended hidden tests, capability-clean mocks, the no-cherry-picking rule), the full 30-task corpus table with tiers and areas, and a results section that renders published runs — with an explicit empty state until one exists: this page never fabricates a number, what appears here has actually run. Prerendered (57 pages), in the nav, SEO-described. The publishing pipeline: web/src/content/benchmark.ts holds the task table and the benchmarkRuns array a local run is committed into; run.mjs now reads each task's tier, aggregates pass@1/pass@2e per tier in both report formats, and prints the publish instruction after every run. The drift gate in consistency.test.ts requires benchmark.ts to mirror benchmarks/gen/tasks exactly — same task set, same tiers (read from each task.md title line) — and any published run to cover the full corpus. Web tests 99.

  6. Sprint 134The corpus grows to 30shipped

    Triples the generation-benchmark corpus, 10 → 30 tasks, now spanning the full language surface: the five media codecs (image_probe, audio_probe, mesh_probe, web_toc on the Block enum, video_probe), the complete capability set under mocks (env_mode/mock_env, clock_iso/fixed_clock, api_status/mock_http, save_report/mock_fs — joining the seed's dice_walk and config_port), generics (uniques[T], swapped[A, B]), the Result surface (parse_point, total_of with ?-propagation), collections and the prelude (histogram, run_length over Text characters, row_sums, set_overlap), the bitwise operators (bit_parity), hex_dump, and async (task_batch — Task capture + tasks.all). Every task stays capability-clean, so the judge touches no network, disk, clock, or entropy. Every task.md now carries a difficulty tier (· tier S/M, the seed retrofitted) so the report can break pass rates down by difficulty from sprint 135 on. The CI gate requires the full set (≥ 30) and runs every reference solution through the exact judge path: 30/30 green — and all twenty new references passed their hidden tests on the first run, the Sprint-133 syntax gotchas applied: the pass@2e thesis in miniature.

  7. Sprint 133The instrument — the generation benchmarkshipped

    Opens Phase M21 (Generation excellence, sprints 133–139) — owner-directed: make POLYTONE the leading LLM programming language, measured rather than asserted. benchmarks/gen/ holds the instrument: a task corpus where each task is a prompt (task.md), a hidden judge (tests.pt, appended to the model's candidate), and a reference solution proving solvability. Ten seed tasks span the surface: texts/prelude (word_stats), maps+lists (grade_book), csv (csv_totals), the json enum (json_pluck), patterns.captures (log_scan), crypto+base64 (content_tag), time (week_later), tuple destructuring (top_scorer), the Rng capability under fixed_rng (dice_walk), and Fs+toml under mock_fs (config_port) — capability-clean, so the judge touches no network, disk, clock, or entropy. The runner (run.mjs, local BYO-key: Anthropic/OpenAI/compatible, never in CI) sends the frozen IDE language card + the task, extracts the code block, appends the tests, and runs ptc test; after a failure the model sees the actual error and gets one repair attempt — pass@1 and pass@2e are both first-class, because the thesis that POLYTONE's teaching errors work is itself under measurement. The CI gate (ptc/tests/gen_corpus.rs) requires every reference solution to pass its own hidden tests through the exact judge path — the corpus can never rot; 10/10 green. Reports always show every task × every attempt.

140–147Phase M22 — Supervised fine-tuning: tool maturity8/8complete

Owner-directed, with screenshots as the first evidence: much still looks simple and rudimentary — go through it in detail, tool by tool, spec by spec. The sixth supervised pass with a new lens: five audits hardened correctness; M22 reviews maturity. Rounds by tool (image/.pti, sound/.pta, 3D/.ptm, video/.ptv, web/.ptw, the playground suite), findings with maturity categories (D depth · U usability · S spec — every format bump additive and version-gated, a teachable line form or nothing · P performance with the concrete algorithmic fix), record M22-REVIEW.md, one sprint per tool so the owner walks each in detail. Provisional: 140 the register + plan (F6.1, the inert Viewer & Player, fixed ahead), 141 .pti v5 + editor, 142 .pta v3 + studio, 143 .ptm v3 + 3D viewer, 144 .ptv v3 + video editor, 145 .ptw v4 + web viewer, 146 the playground suite, 147 proof + the true record + close.

  1. Sprint 147The proof + the true record — Phase M22 closeshipped

    Closes Phase M22 (tool maturity, sprints 140–147). The proof: examples/gallery.pt — one program composing all five upgraded formats: a v5 image (ellipse/outline/polyline/filters), a v3 tune (drums/master/repeats), a v3 film with an embedded soundtrack and an eased move, a v3 model (cylinder + yawed torus through the upgraded rasterizer), and a v4 page (nav/quote/table/spans) embedding the image — main(disk: Fs) the whole effect footprint, six deterministic tests on mock_fs, CI-gated in proof.rs beside pulse/digest/roster/logparse. The M22-REVIEW.md disposition: the format/codec core of every round shipped (five format versions, all additive + version-gated, all tested — images 30 / audio 17 / mesh 16 / video 9 / web 15), plus the live orbit, the stylesheet with dark mode, the v4 demo site, the native 404 page, and the restored Viewer & Player. Carried forward deliberately: the tool-UI pool — human-facing polish cleanly scoped for a dedicated phase. The format layer — the part an LLM writes — is done. Phase M22 complete.

  2. Sprint 146The site eats its own v4shipped

    The R6 window. The demo site the web viewer ships went v4: index and about carry a nav: menu, a quote, a table:, and inline spans — the new format is what visitors actually see (F5.9). The address bar gained datalist autocomplete over the site's documents, and the 404 stopped being a status strip: it is a native .ptw v4 error page — heading, quote, and a nav of the documents that do exist — rendered through the same codec as every other page (F5.7): the browser behaves like a browser. Web 99. The remaining pool (examples browser, native-format tabs, omniview depth, converter fidelity, and the 141–145 tool-UI deferrals) moves to the 147 disposition.

  3. Sprint 145.ptw v4 — real documentsshipped

    Clears M22's R5 core. .ptw v4, additive and version-gated: quote (it was literally the codec's own unknown-block teaching example), note callouts, table: with 8-space head a | b / row x | y lines, nav: menus (item <address> <text>), and inline *bold* / `code` spans in text/item/quote/note — balanced pairs wrap, an odd marker count renders all of them literally. Headings carry deterministic anchor ids (lowercase, hyphenated) so #anchor links finally resolve. And the register's highest look-per-line payoff: the bridge stylesheet — a CSS-variable palette with prefers-color-scheme dark mode, styled inputs/buttons/tables/quotes/nav, heading rhythm, pixelated image rendering; codec-internal, no format change, every rendered page instantly stops looking bare. Two new codec test blocks (web.pt 15/15). Deferred to the 146/147 window: the model block, the viewer shell (autocomplete, 404 page, hash history), demo-site growth, render memoization.

  4. Sprint 144.ptv v3 — the embedded soundtrack + easingshipped

    The format's biggest immaturity dies: a bare audio: opens an indented block of .pta source lines (the sprite-block mechanic, indentation preserved via Text.slice), so the soundtrack travels inside the document — reopening loses nothing; the v2 reference form stays legal (embedded source contains newlines, a reference never does). The editor embeds on export and round-trips on load; the M19 data-loss warning became a positive note. Plus ease in|out|in-out as an optional trailing clause on move (quadratic in/out, smoothstep in-out) — motion stops looking mechanical. All v3-gated with teaching errors; two new codec test blocks (video.pt 9/9); web 99. Deferred to the 146/147 window: also-move riders, wipe, scale, the render caches, the scrubbing player, the preview strip + presets.

  5. Sprint 143.ptm v3 + the live orbitshipped

    Clears M22's R3 core. .ptm v3: cylinder/cone/torus (sphere-rule segments, 0 < r < R for the torus) and an optional yaw <degrees> clause on any shape line — rotation about the shape's own centroid, keeping the grammar one line per shape; all v3-gated with teaching errors. The software rasterizer: per-vertex trig hoisted out of the loop, the O(t²) interpreted insertion sort replaced by a packed-Int host .sorted() walked reversed, and a hemispheric ambient term joined the key light — upward faces catch sky, restoring the depth cue flat shading lost. The viewer: a quarter-resolution render fires on every pointermove (the in-flight guard already existed), the pointerup render settles at full res, low-res frames scale nearest-neighbour — the 771 ms drag-then-wait became a live orbit. Three new codec test blocks (mesh.pt 16/16); api.ts 179 items. Deferred to the 146/147 window: backface culling, vertex normals/Gouraud, zoom/reset/size picker.

  6. Sprint 142.pta v3 — drums, master, repeatsshipped

    Clears M22's R2 core: the sound format stops being a chime demo. Drum waveforms — noise, kick (a sine whose pitch falls from 3× the note to the note), snare (noise + a 200 Hz body), hat (high-passed noise, sharp decay) — synthesized via a deterministic LCG reseeded per event, so renders stay byte-identical under ptc test. master: <0-100> beside swing: as the mix's headroom valve, with the clipping model documented at last (9000 per voice against a ±32000 clamp). song: verse x4 chorus x2 repeat sugar (x1–64, repeats the pattern before it). All additive and version-gated with teaching errors; three new codec test blocks (audio.pt 17/17); the studio accepts v3 documents and the drum waveforms in its parser. Deferred within the phase to the 146/147 window: the chromatic grid, live audition/playhead, track lifecycle, pan/echo/stereo, the wav cache.

  7. Sprint 141.pti v5 — the drawing vocabularyshipped

    Clears M22's R1 core: .pti v5 grows the image format from demo primitives to a real vocabulary, additive and version-gated — ellipse (filled/outline), rect/circle outline variants, line stroke widths (1–64, a square stamp per Bresenham step), polyline (one op per brush stroke), and the whole-canvas filters invert/grayscale/brighten — wired through both dispatch paths (the .pti renderer and the editor's draw_ops) with updated teaching errors. Six new pub helpers, three new codec test blocks (30/30), api.ts regenerated (176 items). The spec gains the v5 section, the layer/(hidden) round-trip convention (documented at last — M22 F1.8), and the corrected glyph claim. The editor emits v5, accepts the new ops in its parser and layer round-trip, and clamps edge taps (an edge tap could produce an out-of-canvas pixel op that failed the whole render). Deferred within the phase to the 146/147 window: drag-preview, redo/keyboard, zoom extras, palette UI, incremental render.

  8. Sprint 140The maturity register + the plan of recordshipped

    Opens Phase M22 (Supervised fine-tuning: tool maturity, sprints 140–147) — owner-directed, with screenshots as the first evidence: much still looks simple and rudimentary. Five audits hardened correctness; M22 reviews maturity. M22-REVIEW.md holds six rounds by tool, ~48 findings from a parallel review, each categorized (D depth · U usability · S spec/version bump · P performance), effort-sized, with the concrete upgrade. Headlines: the image editor gets a v5 vocabulary (ellipse/outline/stroke width/polyline/filters) + drag-preview + incremental rendering; the studio gets drums, live audition, a chromatic grid (today it cannot open its own spec's canonical example), pan/echo/stereo; the 3D viewer gets a live low-res orbit + zoom, vertex normals + hemispheric light, backface culling + a host-sorted painter, .ptm v3 (cylinder/cone/torus + yaw); the video editor gets the embedded soundtrack (.ptv v3 — the data-loss class M19 could only warn about), easing/also-move/wipe/scale, a real scrubbing player, 160×120 defaults; the web codec gets a real stylesheet (dark mode — the highest look-per-line payoff), .ptw v4 (quote/table/nav/model/inline spans), resolving anchors, a browser-feeling shell; the playground suite gets an examples browser, native-format tabs, cross-tool handoff. Fixed ahead: F6.1 — the Viewer & Player app was inert (the data-ov-src hook was missing, init bailed); one attribute restores it. Provisional: one sprint per tool (141–146), proof + close (147).

252–253Phase M40 — The second rotation1/1complete

The open-ended improvement loop returns (the M34 precedent): loose ends, baselines, audits — whatever the numbers ask for next.

  1. Sprint 252M40 opens — one card, actuallyshipped

    The harness sends the evaluated card — byte-identical with the IDE and the Pro CLI; a gate pins the third consumer forever. The methodology notes the comparability break honestly (runs 1–7 sent the raw form). Ride-along: llms-full.txt's five stale format versions corrected. web 157.

249–251Phase M39 — The stream3/3complete

One seam, both surfaces: the CLI streams its completions with an honest progress counter, and the IDE finally wires the SSE transport it has shipped since Sprint 118 into live progress.

  1. Sprint 251M39 close — the review and the proofshipped

    115 shared adversarial streams through both decoder twins, byte-compared: the HTML-502-as-empty-success and the swallowed mid-stream error (both surfaces) fixed; streamed usage clamps; Generate locks during a run; two latent temp-collision bugs found by the proof itself. The committed fixture decodes identically in both suites, and real curl streams it from a live socket. 90 tests, ide 155; phase complete.

  2. Sprint 250The IDE's live progressshipped

    The Sprint-120 carry-over closes: completeStreaming drives the shipped transport end to end, and the panel's status line counts the completion as it arrives. A delta only ever becomes a number — never a code preview; buffered responses and HTTP errors keep their honest shapes. ide 152 tests.

  3. Sprint 249M39 opens — the CLI streamsshipped

    Completions arrive as Server-Sent Events through curl -N, decoded by an exact mirror of the IDE's transport (framing, usage events, tolerance). Progress is a stderr counter — never unverified code; agent mode stays quiet. Absent usage stays flagged-estimate; provider errors surface as their own message. 82 tests.

245–248Phase M38 — The Pro CLI II: routing, ledger, sessions4/4complete

The Pro-parity pass: the IDE's Pro features — cheap/strong routing, the intent ledger, session export/replay — reach the CLI, each mirroring the IDE's semantics exactly and proven by execution.

  1. Sprint 248M38 close — the review and the proofshipped

    Twenty findings by execution, seventeen fixed (three IDE-side): atomic ledger appends under concurrency, media documents re-verified on replay on BOTH surfaces, saturating totals, the estimated flag across routing tiers, all-or-nothing apply with rollback, scratch-path sanitizing, and the session fixture pinned to the exporter's exact bytes. The proof: routed → ledgered → exported → replayed green. 77 tests; phase complete.

  2. Sprint 247Sessions are code, on both surfacesshipped

    'session export' writes the same polytone-session v1 document the IDE exports; 'session replay' re-applies the verified change-sets and re-verifies locally — no model call, and only a green final state is written. One committed fixture is gated from both sides (the IDE's parser and the CLI's, plus a full green replay), so the shared format cannot drift silently. 66 tests, ide 146.

  3. Sprint 246The ledger reaches the terminalshipped

    Every gen/fix run appends one record to ~/.polytone/ledger.jsonl in the IDE's IntentRecord field names verbatim — the foundation for session export. 'polytone ledger' prints totals and the per-model split with the IDE's exact reading rules (corrupt lines skipped, count guards, generation-time verified flag, per-tier attribution). Ungated on purpose: the totals are the honesty layer. 59 tests.

  4. Sprint 245M38 opens — cheap/strong routingshipped

    The Pro CLI gains the IDE's routing: cheap_model goes first, the strong model sees the task only when the cheap repairs fail — cheap tokens, never strong ones. Stop rules mirror the IDE exactly; the ledger itemizes tiers. Pinned by execution: strong is never called on cheap success or provider failure. 54 tests.

241–244Phase M37 — The Pro CLI4/4complete

Owner-directed: a POLYTONE CLI, documented publicly but not available free — the Pro subscription's USP, perfectly integrated into POLYTONEide. The verified token-saving loop in the terminal, behind the same offline license as the IDE.

  1. Sprint 244M37 close — the review and the proofshipped

    Thirteen findings confirmed by execution, eleven fixed: the answer parser now mirrors the IDE's grammar exactly (quirks included), every changed module — subdirectories too — verifies as its own entry, curl carries the full transport hygiene, and the license verifier accepts exactly the IDE's key set. The proof walks the product end to end as a test. polytone-cli 52 tests; phase complete.

  2. Sprint 243The bridge and the pageshipped

    'polytone agent' speaks the IDE's Local-agent protocol (stdin prompt, stdout completion, stderr errors) — the Pro CLI becomes a first-party IDE backend, and a pinned test proves a refusal keeps stdout empty. The /cli/ page documents the whole product bilingually — publicly documented, deliberately not available free — and the Pro plan card gains the CLI line.

  3. Sprint 242The loop reaches the terminalshipped

    'polytone gen' and 'polytone fix': slice + frozen card in, candidate checked and tested in a scratch copy, bounded teach-repair — only a green change-set touches your files. Prompts and repair wording mirror the IDE verbatim; the card is extracted from the IDE at build time. Providers anthropic/openai/compatible via system curl plus a deterministic mock; the ledger reports provider usage and flags estimates. 41 tests.

  4. Sprint 241M37 opens — the Pro CLI's gatekeepershipped

    The POLYTONE CLI opens as the Pro subscription's terminal surface: the free toolchain (ptc) stays complete and free, 'polytone' carries the IDE's verified loop behind the same offline PTPRO license. Sprint 241 ships the gatekeeper — license activate/status/remove, a fail-closed gate that teaches the way in, a hand-rolled SHA-256 + ECDSA P-256 verifier pinned by FIPS vectors and real-issuer fixtures, and a parity gate locking the CLI to the IDE's public key.

218–226Phase M35 — Workshop shine9/9complete

Owner-directed: the tools deserve to look as good as they work — render quality, layout fit, and one wow feature per tool.

  1. Sprint 226M35 close — the true recordshipped

    The proof: one shine session through the committed runtime — deterministic showcase settle, a real turntable film, and a mirrored composition whose exact-family halves agree pixel for pixel. The light, the fit, the wow, and a nine-for-nine executed review: the phase is complete.

  2. Sprint 225The M35 review — nine confirmed, nine fixedshipped

    Every verdict by execution: the film click died on the default fuel while CI ran a toy size — now a real budget with the shipped size pinned; the settle freeze halved; the painter's key packing clamped; stranded repeats dropped; the mirror flag, lead clamp, panel floor, filmstrip scoping, and aria language all corrected.

  3. Sprint 224The song chain becomes visibleshipped

    Chain chips with stable per-pattern hues, dashed repeat badges, red unknown flags, and a plus menu; deletion honors the codec's bound-repeat rule. The input stays the truth — the chips are a live view. All five tools now carry their wow feature.

  4. Sprint 223The filmstripshipped

    Up to twelve evenly spaced thumbnails under the player, decoded from the real rendered film — the film at a glance, any moment one click away, with a live highlight during play, scrub, and jump.

  5. Sprint 222The symmetry brushshipped

    One toggle and every stroke paints its reflection — hooked at the single commit seam, so all tools mirror; each op family reflects precisely and the mirrored ops stay ordinary format lines. Pinned by unit tests per family and a codec end-to-end check.

  6. Sprint 221One screen + the stale-blob fixshipped

    The runtime blob URL is cache-busted by the release version — a cached old blob kept running old semantics, the live error the owner hit. Tool panels cap at the viewport and scroll internally, with a compact headline and a two-line lead: the page itself no longer scrolls on desktop.

  7. Sprint 220The turntable filmshipped

    One click renders twenty showcase frames around the model and exports a real y4m film through the video codec — two codecs, one artifact, reproducible from the exported program. Built on the phase's bulk appends and the thirteen-times-faster painter; pinned end-to-end as valid and deterministic.

  8. Sprint 219The layout passshipped

    Timeline rows wrap inside their panel, the sound raster flexes to fit without sideways scrolling, the image canvas fills its panel with clicks scaling for free, and the shared shell gains depth and title hairlines.

  9. Sprint 218M35 opens — the lightshipped

    A new showcase renderer: sky gradient plus soft ground shadows cast along the key light, supersampled — purely additive, the existing entries byte-untouched. The model workshop settles into it at 640 by 448 with the view filling its panel; the orbit keeps the fast path and a custom background keeps the flat fine render.

198–199Phase M34 — The token economy2/2complete

Highest performance at minimal token spend, measured first: the tokens-to-green instrument, then one approach per sprint — each closed by a fresh measurement. The number decides.

  1. Sprint 199The Fable baselineshipped

    Claude Fable 5 over the full corpus in the workflow harness: cold card-plus-task agents (tool-call-audited — nobody peeked), the exact ptc-test judge, one error-fed repair round. pass@1 21/33, pass@2e 30/33, tokens-to-green median 50372 harness tokens. The first published run on the benchmark page, with a tok-to-green column. All three never-green tasks share one failure class — a value in expression position where return would carry the type — Sprint 200's data-driven target.

  2. Sprint 198M34 opens — the token instrumentshipped

    The benchmark runner accounts tokens per task and attempt from provider usage fields — never estimated — and reports tokens-to-green: what a task costs until the tests pass. Median, mean, per-tier medians, run total, and honest coverage over attempts actually made. The pure module is test-pinned; the benchmark page documents the metric; baseline runs stay owner-run with their own keys. Web suite 144.

194–197Phase M33 — The web viewer4/4complete

The arc's final tool: the shared tool shell, a rendered .ptm path, tabs/history/bookmarks, and a site editor with live preview.

  1. Sprint 197M33 close — the true recordshipped

    The review confirmed eleven defects, every verdict by execution: the S1 edit-burst timer writing into the wrong file after navigation, the Alt-arrow hijack of word-left in the source editor, case-insensitive names against a case-sensitive codec, dead external links in the sandboxed frame, anchors treated as addresses, leaked object URLs, and more — all fixed with regressions pinned. The proof: an authored site session renders deterministically through the real codecs. The M29–M33 tool-overhaul arc is complete. Web suite 141.

  2. Sprint 196The site editorshipped

    The source pane is a live editor: an edit burst writes into the site and re-renders the current address, mid-edit never clobbered. File operations — new, rename, delete, download, reset — hold names to the codec's native-address shape, every mutation forks a bounded site undo, and new files start from codec-valid templates, each E2E-pinned through its own codec. Web suite 139.

  3. Sprint 195Tabs, history, bookmarksshipped

    A tab strip with per-tab history and cursor; the active tab's history listed newest-first with cursor jumps that keep the forward branch (a real push kills it — the exported pushAddress seam); bookmarks behind the ☆ as shell state that survives route revisits; Alt+arrows via the module-level keyboard singleton. Web suite 136.

  4. Sprint 194M33 opens — the web viewer foundationshipped

    The viewer joins the shared tool shell: site file list and live source view left, the browser with a new reload button right; the page title surfaces. .ptm addresses finally render through a mesh bridge — legal in the codec since Sprint 30, a byte-count note until now — and the demo site gained a 3D shrine. The site became mutable state, the site editor's foundation. Web suite 134.

190–193Phase M32 — The video editor4/4complete

The tool arc's fourth phase. Sprints: 190 the foundation (tool shell), 191 easing in the timeline (the v3 clause the editor could import but never author) plus row duplication and a duration sum, 192 the scrub player (frame slider over the decoded film), 193 review, proof and close.

  1. Sprint 193M32 close — the true recordshipped

    The review's two confirmed defects, both fixed: the total readout speaks the codec's truth — per-entry frame quantization with the one-frame floor (the verifier reproduced the low-fps drift by running the real duration logic) — and a duration edit re-renders the timeline immediately. The proof: an eased film with a duplicated move row and an embedded soundtrack round-trips byte-identically and renders deterministically through the real codec, y4m and wav both. Phase M32 complete: the tool shell, authorable easing, row duplication, the honest total, the scrub player. Web suite 130.

  2. Sprint 192The scrub playershipped

    Every frame reachable by hand: the film player gains a scrub slider and a frame-and-seconds readout. Scrubbing pauses playback and paints the exact frame from the decoded .y4m; play resumes from wherever the hand left off. Web suite 129.

  3. Sprint 191Easing becomes authorableshipped

    The v3 ease clause — importable since 0.27.169, never authorable — gets its select on every move row (linear, in, out, in-out), feeding the same TimelineEntry field the round trip already pins. Plus a per-row duplicate button and a total-duration readout beside the timeline heading. Web suite 129.

  4. Sprint 190The video editor — the foundationshipped

    The video editor moves onto the shared tool shell: the library on the left — scenes, sprites, the embedded soundtrack and the live .ptv document — the timeline, player and exports on the right. Pure layout; every seam and test untouched. Web suite 129.

186–189Phase M31 — The sound studio4/4complete

The tool arc's third phase. Sprints: 186 the foundation (tool shell, the long-missing master slider, pattern management), 187 the chain and the holds (xN repeats survive the round trip, note holds enter the grid), 188 the live studio (cell audition, mute and solo), 189 review, proof and close.

  1. Sprint 189M31 close — the true recordshipped

    The adversarial review reproduced four format-truth defects against the live codec — every claim verified by running the real audio.render, not by reading alone — and all four are fixed: pattern delete drops bound repeat tokens (no stranded song chain, no silent rebind to the previous pattern), an emptied chain teaches at render instead of hitting a raw codec error, the hold rule is enforced everywhere (orphaned holds decay when a note cycles off; the import refuses a leading hold with the codec's own words), consecutive repeats are legal (the codec's actual rule — any earlier pattern entry — now mirrored by both the importer and the inline guard), and a pattern cannot claim the repeat form as its name. The proof: a full v3 session round-trips and renders deterministically; master 60 provably differs from full. Phase M31 complete. Web suite 129.

  2. Sprint 188The live studioshipped

    Cells speak when you set them: placing a note plays a short WebAudio preview in the active track's waveform — drums as shaped noise bursts, a kick through a low-pass filter — pure UI feedback born from the click gesture, so autoplay policies are satisfied and the codec's deterministic render stays the only real sound. Mute and solo arrive honestly scoped: per-track toggles filter what Render and Play sends to the sandbox, while the document and every export stay the full truth — the buttons' own tooltips say so. All tracks muted teaches instead of rendering silence. Web suite 127.

  3. Sprint 187The chain and the holdsshipped

    xN repeats survive the round trip: the session keeps the song chain raw — song: beat x3 imports as the two tokens it is, re-exports byte-identically, counts toward the v3 version choice, and the inline chain guard validates repeat tokens by the codec's own rule (x1 to x64, after a pattern name; a repeat after a repeat teaches). Before this sprint, imports expanded repeats into plain names and every re-export lost them. Note holds enter the grid: the equals sign is the codec's own cell form since v1 — the studio used to refuse it on import. A hold row under the note grid toggles it per step (a hold clears the note, a note clears the hold), toPta writes the equals sign, and the old rejection test became a round-trip test. Web suite 127.

  4. Sprint 186The sound studio — the foundationshipped

    The studio moves onto the shared tool shell: tracks, patterns, song chain and the live .pta document left, the note grid and player right. The master headroom finally gets its slider — importable and exportable since 0.23.149, but never adjustable in the page (below 100 the document declares v3; the version follows the content). Patterns become manageable: rename (the song chain follows token for token), duplicate (deep-copied cells), delete (the chain drops the tokens; the last pattern stays). Web suite 127.

182–185Phase M30 — The image workshop4/4complete

The tool arc's second phase. Sprints: 182 the foundation (the shared tool shell, zoom-to-fit), 183 the v5 tools the codec already speaks (ellipse, polyline, outline variants, line width, a canvas eyedropper, whole-canvas filters, redo), 184 the workshop (layer rename and duplicate, pattern stamps, keyboard shortcuts), 185 proof and close.

  1. Sprint 185M30 close — the true recordshipped

    The adversarial review confirmed five real defects — every finding independently re-verified against the live file — and all five are fixed: the redo branch dies when a fresh action forks history (the intended clear had silently not applied; the review caught it), a route revisit resets both stacks, the redo push is bounded, the window keydown listener no longer stacks per visit (one module-level registration retargeted to the latest root), and a crafted layer name cannot fabricate the hidden marker or a comment line. The proof: every op form the new UI emits renders green through images.draw_ops, and one full session renders deterministically through renderProgram. Phase M30 complete — the tool shell, zoom to fit, the whole v5 vocabulary drawable, redo, layer rename and duplicate, stamps, shortcuts. Web suite 127.

  2. Sprint 184The workshopshipped

    Layers become workable: rename (the name stays one clean token for the layer-comment convention, escaped in the row as ever) and duplicate (deep-copied ops). Pattern stamps — frame, grid, sun — append parametric op groups sized to the canvas: ordinary ops on the active layer, carried by undo, redo and the document like anything drawn. Keyboard shortcuts: one key per tool (b, f, c, r, l, g, p, t, e, y, i) and Cmd/Ctrl+Z with Shift for redo, ignored while a field has focus. Web suite 125.

  3. Sprint 183The v5 tools arrive in the UIshipped

    The codec's whole v5 vocabulary becomes drawable: an ellipse tool (two clicks — center, then radii), a polyline tool (clicks, double-click closes, two points and up), an outline checkbox for rect, circle and ellipse, and the size slider now gives line its v5 width (one to sixty-four, a square stamp per step). An eyedropper picks any rendered pixel into the color well; four filter buttons — invert, grayscale, brighter, darker — append whole-canvas v5 ops to the active layer, so undo, redo and the document carry them like any op. And redo joins undo: a bounded branch that dies when a fresh action forks history. Web suite 125 green.

  4. Sprint 182The image workshop — the foundationshipped

    The editor moves onto the shared tool shell: a two-panel workshop — tools, palette, layers and the live .pti document left, a large scrolling canvas right — plus zoom 8x and Fit, and a v5-accurate lead. Every pure seam and safety pin unchanged; web suite 125 green untouched.

178–181Phase M29 — The model workshop4/4complete

The first phase of the owner-directed tool overhaul — one tool per phase, the 3D tool first: its render was reported broken (diagnosed as the dropped-render class of v0.25.159, fixed in 0.27.169, now pinned by permanent tests). Sprints: 178 the foundation (two-panel workshop layout, wheel zoom on a new stdlib surface, curated examples, .ptm import), 179 rendering quality (supersampling, a fill light, background control, turntable), 180 the workshop (the model block as an editable shape list, palette editor, undo), 181 proof and close.

  1. Sprint 181M29 close — the true recordshipped

    The proof: one end-to-end test walks a whole workshop session — load a curated scene, edit it the way the shape list does (add a torus, recolor a shape), regenerate the document, and settle a fine render at a chosen zoom and background; every step is the tool's own code path and the output is deterministic. The phase's record: the reported render failure diagnosed (the dropped-render class of v0.25.159, fixed since 0.27.169) and pinned by eleven permanent tests, the two-panel workshop with the shared tool shell, wheel zoom and camera HUD, render_view_fine with supersampling, fill light and chosen background — the fast path byte-stable throughout, the differential never moved — a turntable, three curated scenes, .ptm import, and the model block as an editable, round-trip-pinned shape list. Next, per the arc plan: M30, der Bildeditor, sprints 182 to 185.

  2. Sprint 180The workshopshipped

    The model block became an editable shape list: a row per document line — kind, numeric fields in the grammar's own order (all seven shapes, including triangle's at-less form), optional yaw, color — with per-field inputs, duplicate and delete, add-shape buttons with sensible defaults, and a fifty-step undo. The source stays the single truth: the list is a parse of it (parseShapes), every edit regenerates it (buildModel), and a line the grammar does not cover survives untouched as raw text. The round trip is pinned by tests over every curated example, and every default shape renders green through the real codec. Web suite 124.

  3. Sprint 179The view learns to look goodshipped

    mesh.render_view_fine: two-times-two supersampling (the same rasterizer at twice the size, box-averaged down — crisp edges from identical geometry), a soft fill light so undersides keep their color, and a chosen background, RGB clamped. The fast path is untouched — render_view_from delegates into a shared parameterized core that stays byte-for-byte what it always produced, so the differential held 46 of 46 without touching a fixture. The workshop settles through the fine path while the quarter-resolution orbit stays fast; a background picker joins the toolbar; and a turntable toggle auto-orbits the model on a timer, honoring prefers-reduced-motion, stopped by any drag. api.ts 181 items, mesh.pt 21 tests, web suite 121.

  4. Sprint 178The model workshop — the foundationshipped

    The 3D report diagnosed and pinned: the screenshots' v0.25.159 carried the dropped-render class (a render requested mid-flight was silently discarded — a cold start could strand a blank view), fixed in 0.27.169 and live; permanent end-to-end tests now pin the render program, every curated example, and deterministic zoom, so the class cannot return unnoticed. The viewer became a workshop: a real two-panel layout — document left, large view right, the shared tool shell every coming phase reuses — a 480 by 360 view scaling to its panel, wheel zoom backed by mesh.render_view_from (zoom as a factor on the automatic framing distance, 1.0 exactly render_view by delegation, clamped 0.2 to 8.0 — the differential stayed 46 of 46), three curated example scenes, .ptm file import, and the camera HUD. api.ts 180 items, web suite 119.

173–177Phase M28 — Footprint & playground5/5complete

Owner-directed: optimize the compiler's footprint as a measured loop — it consumed absurd amounts of space and thus valuable resources — and expand the playground so it can do much more. The loop's rule: measure, fix the biggest consumer, verify, repeat. Sprints: 173 iterations 1–3 (the 44-gigabyte target directory cleaned and its cause removed — the frozen crate version; the shipped wasm blobs get a size profile, minus 41 percent), 174 iteration 4 — gates so nothing regrows (blob-size budgets, target hygiene, bench verification), 175 playground I (Format and Check beside Run, deterministic capability controls for Clock, Rng, Env and Fs), 176 playground II (the output-files panel: images, page previews, audio, video, downloads), 177 proof and close.

  1. Sprint 177M28 close — the true recordshipped

    The proof, then the record. One end-to-end test is the phase's proof: a single playground session whose main takes all four sandbox capabilities — the clock knob dates the report and seeds the dice, env names the author, a data file feeds disk.read — and which Check type-checks without a single print, Format fixes as a fixpoint, two seeded runs reproduce byte for byte, and whose written .pti renders to a real .ppm through the panel's own codec path. First-run green. Phase M28 is complete: the footprint loop took compiler/target from 44 gigabytes to a sub-gigabyte steady state and removed the cause — the frozen crate version plus the VERSION file, proven at about five seconds per bump; the shipped web runtime shrank from 1770 to 1160 kilobytes on the measured s-profile; and gates bound it all — blob budgets that fail, a hygiene note that warns. The playground became a workbench: Format, Check, the sandbox knobs, data files, and an output panel that renders every format through the real codecs. No next phase is scheduled — the owner directs what follows.

  2. Sprint 176The output panel learns every formatshipped

    A written native document renders in place. A program that writes a .pti, .pta, .ptm, .ptw or .ptv through disk gets a Render button on its file card: the playground runs the Viewer & Player's own per-format program in the sandbox — the real codec, nothing reimplemented — and previews its outputs with the very card builders the panel already has: canvas for .ppm, an audio player for .wav, a sandboxed page for .html, playable video for .y4m. The outputs carry no further Render button, so the path cannot recurse. Text outputs are readable, not just downloadable: every written text-format file gains a collapsed source preview capped at twenty thousand characters, and .bmp files — the images bridge's own format — preview natively. Two end-to-end tests pin the loop (a program writes a .pti; the panel's render path turns it into a real .ppm through the image codec) and the format table. Web suite 113.

  3. Sprint 175Playground v3 — the whole toolchain in the pageshipped

    Format and Check join Run, Tests and Share. Format runs the same polytone-fmt the CLI runs — a new polytone_fmt entry point in the web runtime, fifteen kilobytes inside the size budget — so the playground's canonical form and ptc fmt cannot drift. Check type-checks through the context slice without executing anything, marking the first diagnostic's line. The sandbox knobs make capabilities real in the browser: a clock field (RFC 3339, empty means the epoch) seeds the sandbox clock and, through the host's derivation, the Rng seed too — one knob makes both deterministic; an env field feeds sys.var; and the module bar accepts data files (any non-pt extension) for disk.read. A main taking Clock, Rng, Env or Fs now runs meaningfully in the playground, deterministic by construction. The run protocol gained one field — clock_seconds — across the wasm runtime and both JS bridges, and six end-to-end tests pin the surface through the committed blob: the clock knob steers Clock and seeds Rng reproducibly, env reaches Env, Format is a fixpoint on canonical input, and Check names the right line without running a single print.

  4. Sprint 174The loop's exit gate: measured, bounded, gatedshipped

    The loop benched its own iteration-3 choice and corrected course: optimize-for-size z cost 4 to 28 percent compiled-backend runtime (text_scan 249 to 319 milliseconds) for only about 9 percent less size than s — so the blob profile is now opt-level s, within 3 percent of the untuned blob's speed at minus 35 percent size: web runtime 1770 to 1145 kilobytes, VM runtime 314 to 253. The measurement lives as a comment on the profile itself. Preflight gained wasm blob size budgets — web at most 1300 kilobytes, VM at most 320; over budget fails the gate, and raising a budget demands a measured changelog note — plus a non-failing target hygiene note past 10 gigabytes naming cargo clean (the steady state is under one). The VERSION-file routine proved itself on this sprint's own bump: about five seconds, one leaf crate, seven megabytes of target growth, where a bump used to re-fingerprint the world. Differential 46 of 46.

  5. Sprint 173The footprint loop, iterations 1–3shipped

    Measured first: compiler/target had grown to 44 gigabytes — 43 of them debug artifacts across 411,287 files — because every sprint's workspace-version bump re-fingerprints every crate, the whole workspace recompiles, and stale artifacts never leave (about 170 sprints times 250 megabytes). Iteration 1: cleaned to 24 megabytes. Iteration 2, the cause: the crate version is frozen at 0.0.0 on purpose; the real toolchain version lives in the repo-root VERSION file, injected at build time into exactly the three crates that display it (ptc, lsp, mcp — a 15-line build script each). A sprint bump now recompiles three leaf crates instead of the world, records-only sprints stop invalidating the wasm stamp, and the docs-record gate holds VERSION to the changelog so ptc version can never lie. Iteration 3, the shipped bytes: a dedicated wasm-blob profile — optimize-for-size, fat LTO, one codegen unit, stripped, panic=abort — shrank the web runtime from 1770 to 1042 kilobytes (minus 41 percent) and the VM runtime from 314 to 237; release gained thin LTO and stripping (ptc 2.15 to 1.90 megabytes). The differential stayed 46 of 46 against the optimized blobs: semantics are pinned by the gate, not the optimizer.

168–172Phase M27 — The debt pass5/5complete

What M25/M26 make worth paying, plus what is owed. Sprints: 168 the hashed Set/Map backing — deferred since M12 F1.7, five phases, now tractable because structural equality and Ord give the key contract; 169 the open M23 register R3–R6; 170 Num and Byte semantics, spec §9's remaining deferral; 171 a benchmark round on the new surface — corpus tasks that need methods and traits, measuring whether the priors now match; 172 close.

  1. Sprint 172M27 close — the abstraction arc completesshipped

    The true record. Phase M27 (the debt pass) is complete, and with it the whole M25→M26→M27 plan of record is fully executed: every user type owns its behaviour, traits abstract over it with static dispatch, and the debts those features made worth paying are paid — the hashed Set/Map backing (a five-phase-old finding closed with nothing observable changed), the whole M23 register (fourteen findings, each with a regression test), Num and Byte settled by decision, and a benchmark corpus of 33 with tasks that need the new surface. The codec stayed v9 across all three phases; the differential grew from 36 to 46 fixtures, every one first-run. Deliberately out, where their records name them: pub bounded functions, user traits across modules, trait-side type parameters (spec §30.5); intent blocks and the formal memory model (§9). A records-only close. No next phase is scheduled — the owner directs what follows.

  2. Sprint 171The benchmark learns the new surfaceshipped

    Three corpus tasks that need methods and traits, so the generation loop can measure whether the M25/M26 priors now match. shape_area (tier S): a record method, s.area() summed over a list. money_order (tier M): with Ord: and with Display: on a record — the hidden tests force the trait forms, a Money spelling as $0.05 with two-digit cents and .lt() called as a method. season_label (tier S): enum Display reading a payload through match self:. Every reference passed its own hidden tests through the exact judge path on the first run; the corpus grows from 30 to 33, the CI gate covers all of them, the benchmark page gains a Methods & traits area, and the drift gate holds the site table to the task directories row for row. Fresh pass@1 and pass@2e runs stay owner-executed, BYO-key, as every phase records.

  3. Sprint 170Num and Byte settled — never typesshipped

    Spec §9's last language deferral closes by decision, not arrival — an LLM-first decision. A Num supertype would reintroduce exactly the Int/Float coercion ambiguity the split exists to prevent, and a scalar Byte would give one value two spellings: a Bytes element is already an Int 0–255. Both names stay reserved with permanent teaching errors naming the form to write — the Num error no longer says deferred in v0.1. Spec §8.1 records the decision; §9 now holds only intent blocks and the formal memory model.

  4. Sprint 169The M23 register closesshipped

    All fourteen open M23 findings (rounds 3–6), each with a regression test — the register M23-REVIEW.md carried them since the M24 close, and its new disposition records the closure: nothing from M23 remains open. Mesh: out-of-order trailing clauses teach the order (… yaw <degrees> color <#rrggbb>), the packed sort key's bounds are documented in place, and the 3D viewer queues a render requested mid-flight and runs it when the pass settles, full resolution winning. Video: the editor imports and round-trips a codec-valid ease move with the v3 gate; an audio: block after scenes, sprites or the timeline is an error, not silently swallowed; blank lines inside the embedded score survive both directions; the stale v2 wording is gone; the codec's ease arm keeps the from/to keyword checks; and the film program renders byte-for-byte the score the .ptv embeds — one normalized source, two uses. Web: a * inside a code span is literal (star balance counted over effective markers), so inline spans always nest; duplicate headings get numbered anchors; the table parser teaches one-head-first-then-rows and demands a head. Benchmark infra: all five format-doc H1s name their newest version, a zero-test judge run fails instead of passing vacuously in both the runner and the CI gate, scratch candidates carry model and PID, the drift gate pins headline pass counts to the rows' own arithmetic, and the fence regex tolerates trailing space and CRLF while a typo'd --only errors loudly.

  5. Sprint 168The hashed Set/Map backingshipped

    M27 opens by paying the oldest performance debt: sets and maps are hash-indexed, closing M12 F1.7's second half after five phases. Elements and keys carry a structural hash mirroring equality exactly — order-independent for sets and maps, -0.0 files with 0.0, and any value can still be a key: records, tuples, nested collections. Membership, get, has, add and key update are O(1) expected instead of a linear equality walk; building n elements is O(n) instead of O(n²). Nothing observable changed, and the spec needed no edit beyond its version line: iteration order is still insertion order, dedup still keeps the first occurrence, a repeated map key still keeps its position and takes the last value, equality is still order-independent — all pinned by tests. The second copy-per-iteration hole closed with it: a mutating method on a plain local now runs in place, the receiver taken out of its slot for the call — existing opcodes only, the codec stays v9 — whenever no argument mentions the receiver; s.add(s.len()) keeps the copying path and still sees the old value. A 20,000-element workload dropped from 7.5 seconds to 0.03, an algorithmic win. The fixture passed the differential on its first run: 46 fixtures.

161–167Phase M26 — Abstraction II: traits7/7complete

The item spec §9 deferred since Sprint 1 — closed. M25 gave every user type its methods; M26 abstracts over them: a trait names a behaviour contract, implemented in a with Trait: block inside the type's own declaration; generics become bounded, so a generic function may finally call something on its type parameter; and the built-in Ord and Display cash the payoff — one bounded sort for user, builtin, and stdlib types, user spelling in interpolation. Static dispatch only, resolved by the existing monomorphizer: one form, no vtable semantics to explain, no runtime cost — the lowerer, PTIR, codec (v9 the whole phase), VM and compiled backend learned nothing, and every fixture passed the differential on its first run (39 to 45). Sprints, as they actually landed: 161 trait declarations and with blocks, 162 bounded generics, 163 Self and the built-in Ord (equality is deliberately structural, never a trait), 164 Display (show, not to_text), 165 multi-bound and generic types under bounds, 166 the payoff — builtins and imported stdlib types under bounds, the leaderboard proof, guide and card v9 — and 167 the close. Complete at 0.26.167.

  1. Sprint 167M26 close — spec §9 loses its oldest entryshipped

    The true record. Phase M26 (Abstraction II — traits, Sprints 161–167) is complete, and spec §9 loses its oldest entry: traits, deferred there in Sprint 1, are closed across §30 — declarations and with blocks held to the exact contract, bounds and multi-bound, Self, the built-in Ord and Display, and builtins, generic types and imported stdlib types under bounds. Static dispatch through the existing monomorphizer the whole way: the codec stayed v9 all phase, the lowerer, PTIR, VM and compiled backend learned nothing, and the differential grew from 39 to 45 fixtures, every one first-run. Deliberately out, recorded in §30.5: pub bounded functions, user traits across modules, instantiated imported generics under Display in interpolation, and trait-side type parameters. A records-only close; next, per the plan of record: M27, the debt pass — hashed Set/Map backing now that Ord gives the key contract, the open M23 register, Num and Byte semantics, and a benchmark round on the new surface.

  2. Sprint 166The payoff: builtins, stdlib, and the proofshipped

    The scalar builtins carry the built-in traits they honestly support: Int, Float and Text are Ord — the types < orders — and those plus Bool are Display, so one bounded sort serves a user type, Int, Text, and a stdlib type alike. The compiler synthesizes the method (Int.lt IS <, Int.show IS the spelling) only where a bound actually lands — an unused bound costs nothing — and Bool under Ord is a teaching error naming the supported set. An imported pub type satisfies a built-in trait bound through the with block it exported: built-in traits mean the same thing in every module, so the impl travels with the type, its methods needing no pub of their own (the contract is total). A user trait stays module-internal, with a teaching error that says only the built-in traits travel. The stdlib cashes it: time.Instant is Ord and Display (sorts on the timeline, spells its .iso() form in interpolation), time.Duration is Ord, http.Status is Display. The fixture passed the differential on its first run — 45 fixtures. The M26 proof, examples/leaderboard.pt, composes the phase's whole surface with the design claim as a test: the spelling is a call, dispatch is static. The guide gained a Traits section, and the IDE language card bumped to v9 to teach it all. pub on a bounded function now teaches the real reason it stays module-internal — an importer's types cannot reach its instantiations — and the way out: export an unbounded wrapper.

  3. Sprint 165Multi-bound; generic types under boundsshipped

    A type parameter may now carry several traits, joined with + — fn podium[T: Ord + Display](a: T, b: T) orders with one bound and spells with the other. A call on the parameter resolves against all of its traits, and ambiguity cannot arise: two traits that declare the same signature cannot be bound together, a teaching error at the function, and repeating a trait teaches that one mention grants the whole contract. Propagation carries the whole list, and the error's fix joins it — declare this function's parameter as [U: Ord + Display]. An instantiated generic now satisfies a bound through its base's with block: Ranked[Int] implements what Ranked implements, clearing Sprint 162's defer error. The bound's method is itself a template, instantiated per call through the same bindings that substitute the types — still fully static. This also closed a silent gap from Sprint 164: a generic owner's Display typechecked but interpolation fell back to the structural spelling; now the segment records its template instance and the spelling arrives. The fixture passed the differential on its first run: 44 fixtures. Deliberately out: trait-side type parameters (Self already gives a trait's signatures the implementing type), builtin types under bounds and cross-module traits — the payoff sprint decides those.

  4. Sprint 164Display: a type spells itselfshipped

    Display is the second built-in trait: a type that implements it chooses its own text form in interpolation. with Display: holds one signature — fn show(self) -> Text, the text this value spells as in "{...}" — and an interpolation segment whose type implements Display IS the call: "{m}" is "{Money.show(m)}", rewritten by the monomorphizer where methods hoist, so the lowerer, PTIR, codec, VM and compiled backend learned nothing; the fixture passed the differential on its first run, 43 fixtures. A [T: Display] bound carries the spelling into a generic body — inside, "{x}" resolves through the bound, and at every call site the inferred type argument must implement Display, checked like any bound. The segment's own type decides, statically: a type without Display keeps the structural spelling, and so does a container of Display elements — there is no runtime dispatch to reach inside a value; interpolate the element for the chosen form. print takes Text, so the spelling arrives through the same door, and print of a non-Text value now teaches it — interpolate the value instead — where the old mismatch suggested changing the declared type. The method is named show, not to_text, an LLM-first decision: the prelude's Bytes.to_text returns Option[Text], and one name must keep one shape.

  5. Sprint 163Self and the built-in Ordshipped

    Self now names the implementing type in any member signature — a plain method's (fn twin(self) -> Self), a trait signature's, a with block's — substituted where methods hoist, so nothing below the type checker learns it. On a generic owner it arrives fully applied (Self inside Pair[A, B] is Pair[A, B]); inside a bounded function a bound's Self is the parameter itself, so fn lt(self, other: Self) on a T receiver takes another T; and Self is never a declarable name — record, enum, trait or type parameter, each a teaching error. Ord is the first built-in trait: predeclared in every module and implemented like any other, with Ord: holding one signature, fn lt(self, other: Self) -> Bool — whether self orders strictly before other — from which any sort, minimum or comparison chain builds through a [T: Ord] bound. Redeclaring a built-in trait teaches the with form. The fixture sorts two different user types through one bounded insertion sort and passed the differential on its first run: 42 fixtures. Equality is deliberately not a trait: == and != are already structural for every value (spec §11), so an Eq bound would grant nothing a type does not have — documented as an LLM-first decision in §30.2; the key contract for hashed collections stays a separate M27 decision.

  6. Sprint 162Bounded generics: [T: Shape]shipped

    A generic function's type parameter may now carry a trait bound — fn total_area[T: Shape](shapes: List[T]) — so it can finally call something on its parameter instead of only moving it around. Inside the body a T-typed value has exactly the bound's methods: s.area() resolves against the trait's signature, mut self under the usual mutating rules, and on an unbounded parameter a method call stays a teaching error (its values can only be passed along — the old rule, unchanged). At every call site the inferred type argument must implement the bound: a record or enum with the with block, or another type parameter carrying the same bound, which is how a bounded value flows through helper functions. Violations teach the exact fix — add 'with Shape:' to Plain's declaration; declare this function's parameter as [U: Shape]. Static dispatch, zero cost: the checker records a bounded call as T.method, and the monomorphizer substitutes the concrete owner per instance through the same bindings that substitute the types — each instantiation calls the implementing type's own method directly, no vtables, no runtime dispatch, nothing below the type checker changed. The fixture (two instantiations of every template, bound propagation, mut self through a bound) passed the differential on its first run: 41 fixtures. Scope held honest by teaching errors: bounds live on functions, a bounded function stays module-internal until cross-module traits arrive later in the phase, and a generic or imported type as a bounded argument defers likewise. Spec §30.1, §24 reconciled.

  7. Sprint 161Traits: trait declarations + with blocks (opens M26)shipped

    Traits land — the item spec §9 had deferred since Sprint 1. A trait names a behaviour contract: trait Shape: opens an indented list of method signatures — docs allowed, bodies not (a body is a teaching error naming the with block it belongs in), mut self receivers included, and a trait shares the type namespace: one name, one thing. A type implements it inside its own declaration: the with Shape: block after its fields or variants and its plain methods — behaviour stays where the type is, the same one-canonical-place rule methods follow. The block must implement exactly the trait's signatures: every one present, nothing extra, same receiver form, same parameter types, same return type — each violation a teaching error naming both sides, like "'C' implements trait 'Shape' but is missing 'area' — add 'fn area(self) -> Float' to its 'with Shape:' block". An implementation IS a method IS a function: a with block's methods join the type's method surface — same one-name-one-thing rules, same context-slice and ptc doc presence, same Type.method hoisting path — so the lowerer, PTIR, the codec (v9), the VM and the compiled WASM backend learned nothing, and tests/fixtures/traits.pt (a record, an enum matching self, and mut self through a trait) passed the differential on its first run: 40 fixtures. Spec §30; twelve new type-checker tests, six parser tests; the IDE's trait and with keyword hovers teach the forms. Next: bounded generics [T: Shape].

155–160Phase M25 — Abstraction I: methods on user types6/6complete

Owner-directed: plan the next phases and develop them autonomously — make POLYTONE's capabilities rise massively. The plan of record covers M25 methods, M26 traits, and M27 the debt pass. The gap was measured, not guessed: the tooling is mature and the compiled backend is not a subset, but spec §9 has deferred traits since Sprint 1 and §13/§15 still said records and enums have no methods in v0.1 — every user type inert data, every behaviour a free function. Methods come first because they are what a trait abstracts over. Sprints: 155 record methods, 156 enum methods and mut self, 157 the stdlib payoff, 158 the surface (spec, guide, card, completion), 159 generic methods, 160 the proof and close. Complete at 0.25.160.

  1. Sprint 160The proof (closes M25)shipped

    Closes Phase M25. The proof is examples/itinerary.pt — one program in which every behaviour lives on the type it belongs to. A day-trip planner: an enum method reads its payload with match self (Mode.pace, Mode.label), a record method calls a method on its own field (Leg.duration reaches self.mode.pace()), a mut self method builds the plan in place (Plan.add), a generic method infers from its receiver and reorders its type's parameters (Ranked[K, V].flipped() -> Ranked[V, K]), and the stdlib's own methods chain across modules (Instant.plus, Duration.in_minutes, Table.text/int/keys on the parsed TOML output). The phase's design claim is itself a test — t.plus(d) == time.add(t, d), a method IS a function — and the program is pure, no capabilities and no mocks, so its seven tests are deterministic by construction; all seven passed on the first run, and the proof suite gates it in CI beside pulse, digest, roster, logparse and gallery. The phase record: spec §13/§15 no longer say records and enums have no methods; the codec stayed v9 the whole phase and the differential grew 36 to 39, each new fixture passing on its first run — the design (a method is a function, hoisted on the lowering path only) held end to end. Next, per the plan of record: M26 traits.

  2. Sprint 159Methods on generic typesshipped

    A method of Pair[A, B] is a generic function over the declaring type's own parameters, inferred at every call from the receiver — so a return type may name them and even reorder them, as in fn swapped(self) -> Pair[B, A]. It monomorphizes like any other generic call: the receiver's instantiated key maps back to the base name through the meta table, the call routes through the same inference every generic call uses with the receiver as its first argument, and the monomorphizer's instance rename claims the same call site, so a generic method still ends up a plain, specialized function. The fixture covers two instantiations of one record template, a parameter-reordering return type, and a generic enum whose method matches its payload; differential 39. The two teaching errors that named this limit are gone, and their tests now assert the capability instead.

  3. Sprint 158The surface learns methodsshipped

    The IDE completes and hovers a project's own methods: after any receiver that is not a module or an enum, a type's methods come first and the prelude's behind them, each labelled with the type it belongs to — the same honesty the prelude receivers already had, since the context slice carries no expression types. The language card went to v8 and teaches the method form: declared after the fields or variants, a bare self, match self for an enum's payload, mut self for a method that rewrites its receiver, and the three rules that keep a type's surface unambiguous. Fixed: an enum's method lines were read as variants — since Sprint 155 a type's surface carries fields or variants and methods, one per line, so typing Status. offered a method head as though it were a variant; member lines are now told apart by shape. IDE tests: 143.

  4. Sprint 157Methods cross modules; the stdlib payoffshipped

    A pub method of a pub type joined its module's surface under the same Type.method name it carries everywhere else, so an importer resolves r.area() on a shapes.Rect with no import-specific rule — mut self included. The stdlib then put it to work, every method delegating to the free function that already existed so nothing was removed or renamed: time.Instant gained civil, iso, plus, minus, until, is_before and is_after; time.Duration gained in_minutes, in_hours, in_days and abs; http.Status gained ok, client_error and server_error; http.Response gained ok, text and header; http.Request gained with_header; and toml.Table gained get, has, text, int and keys. wall.now().plus(time.days(1)).iso() is now the natural spelling of what took three nested calls — and the one a model reaches for first, which was the whole argument for methods. ptc doc shows a type's methods where the type is, each with its doc on the member line; the API reference was regenerated at 24 modules and 179 items. Stdlib tests: time 8, http 5, toml 8.

  5. Sprint 156Enum methods and mut selfshipped

    An enum declares methods after its variants, exactly as a record does after its fields — same self receiver, same rules — and a method reaches a payload the only way anything does: by matching self. A method declared mut self rewrites its receiver: it returns Void, needs a block body, and follows the rules the mutating prelude methods follow, meaning a named binding declared mut, never a temporary and never a lambda capture. Values are values, so rewrite is a call and a store: c.bump(5) is exactly c = Counter.bump(c, 5) — the receiver arrives under a private name, is copied into a mut self local the body mutates, and is returned, with the monomorphizer turning the call site into the assignment. No aliasing is introduced anywhere, and once again nothing below the type checker learns a new concept, so the compiled backend ran the new fixture unchanged (differential 38). The guards are all teaching errors: an immutable or temporary receiver, a non-Void return type, an expression body, and using the call's absent result.

  6. Sprint 155Methods on records (opens M25)shipped

    Phase M25 opens the abstraction arc, and the gap was measured rather than guessed: spec §9 has deferred traits since Sprint 1, and §13 still said plainly that records have no methods in v0.1 — every user type inert data, every behaviour a free function, so a model writing POLYTONE had to abandon its strongest prior (that a type owns its behaviour) on every program. Methods come first because they are what a trait abstracts over. A record now declares its methods after its fields, in the same block, taking the receiver as a bare self: a class body is the dominant prior across corpora, it needs no new keyword, and it keeps one canonical place to look for a type's behaviour, unlike free impl blocks of which there may be many, anywhere. The rules are teaching errors — self first and never annotated, fields before methods, and one name means one thing (no method/field clash, no duplicate method) — while two different records may both declare area(), the receiver's type selecting it, which is the whole point. A record's methods joined its context slice surface, so the IDE's completion and hover see them with no change. The design is the result: a method IS a function. r.area() means exactly Rect.area(r), hoisted by the monomorphizer on the lowering path only, so fmt still round-trips the source as written; the lowerer, PTIR, the codec, the VM and the compiled WASM backend learn nothing at all, the differential passed methods on its first run (37 fixtures), and a method costs exactly what a call costs. No codec change. Ride-along fix: a postfix chain gave every link the same source position, so .foo in a.b().foo() reported at a; each field access now carries its own — better errors, and what makes per-call-site method resolution possible at all.

150–154Phase M24 — The programming experience5/5complete

Owner-directed: what is still urgently missing for us and for users to actually program with the IDE and POLYTONE? The answer came from sitting down and trying, not from guessing — three verified gaps: no call stack on a runtime error (the VM held the frames and never emitted them, blocking humans and the pass@2e repair loop alike), no ptc new (fourteen commands, no scaffolding, so a first-timer guesses every convention), and an IDE editor without code intelligence (104 lines of textarea, highlighting and Tab, while a full LSP ships in the same repo). Shipped: 150 call stacks, 151 ptc new, 152 IDE inline diagnostics, 153 IDE completion + hover, 154 the first-session proof — a session executed as a test, so the walkthrough cannot go stale — plus the true record and close. Complete at 0.24.154. The open M23 audit register rode along and is recorded in M23-REVIEW.md's disposition: rounds 1 and 2 cleared, three findings taken by M24, rounds 3 to 6 open with full statements and no S1 among them. No next phase is scheduled — the owner directs what follows.

  1. Sprint 154The first session (closes M24)shipped

    The phase opened with a question — what is still urgently missing to actually program with POLYTONE? — and answered it by sitting down and trying. So the proof is that session itself, executed as a test: first_session.rs walks ptc new notes, then ptc test green before a single edit, then real code in with a comparison wrong, then ptc check naming it with a position and NOT running the program (asserted: the program's own output must be absent from a check), then ptc context carrying the signature and doc the editor completes and hovers from, then ptc run failing two frames deep and printing the call stack (asserted: three frames, innermost first, each positioned), then the guard going in and the session ending green and canonical. A matching "Your first session" guide section teaches the same seven steps to a human, including getting it wrong twice; the guide's opening line, which still claimed "no project scaffolding" — true until Sprint 151 — is corrected. M24 is complete: three gaps found by trying, all closed, plus the ride-alongs — M23's F3.1, a capability that could take a record's name, and two remote-only gates brought into preflight, now six. M23 stays partial and says so in M23-REVIEW.md: rounds 1 and 2 cleared, F3.1/F7.1/F7.2 taken by M24, rounds 3 to 6 open with full statements and no S1 among them.

  2. Sprint 153Completion and hover in the IDEshipped

    Sprint 152 taught the editor to report a mistake; this teaches it to prevent one — from the same source. One context slice per idle pause now answers both questions, what is wrong and what is in scope, so completion costs no extra crossing of the sandbox boundary. Candidates come from the slice's own modules[].items[] — kind, signature, members, and the /// doc — in three tiers by how much can honestly be known: after module. the module's pub surface exactly; after Enum. its variants, the one place completion can be precise about names that are always written qualified, where a unit variant inserts without parentheses because Status.Active() is a teaching error; and bare, the file's own declarations plus the imported module names — never an imported item unqualified, which would suggest code that does not compile. Ranking is case-exact prefix, then case-insensitive, then substring. Ctrl/Cmd-Space opens the list, arrows navigate, Enter/Tab accepts (a callable brings its parentheses and parks the caret inside them), Esc dismisses. Pointing at a name shows its signature, its fields or variants, and its doc — the editor is monospace, so one measured advance maps pixels to a text offset both ways. The frozen prelude is deliberately absent from every slice, so symbols.ts states it once: every keyword, builtin type, builtin function and prelude method, each documented, with a test that none is undocumented or misspelled. Eleven new tests (ide 140). Two fixes found en route: a capability could take a record's name (record Env: and enum Rng: were accepted where record Fs: was refused — collect_types carried a private copy of the builtin-type list that never learned about Env and Rng when they joined the capabilities in M13; one list now feeds both), and cargo fmt --check was CI-only, so a Sprint-151 drift sat on develop for two sprints — preflight runs it now, at six gates.

  3. Sprint 152Inline diagnostics in the IDEshipped

    The third gap opens: the workbench editor was a textarea with syntax highlighting and Tab — you learned about a mistake by running the program, while a full type checker sat in the same sandbox the run panel already loads. Now, after every idle pause, the workbench type-checks the project through the context slice (spec §26) — which resolves imports and checks without executing, so typing never runs your program — and paints a marker on each reported line, the message on hover; a status line under the editor names the first problem and counts the rest, and clicking it jumps the caret there. The DOM-free core (ide/src/core/diagnostics.ts) carries the logic that deserves tests: parseDiagnostics never throws (a malformed payload yields no diagnostics rather than breaking the editor mid-keystroke), checkFiles passes source files only and swallows sandbox failures, summarize names the first problem, and debouncedCheck collapses a burst into one run, drops a superseded result, and cancels on file switch. Five new tests (ide 129); the editor gained setDiagnostics and goTo(line, col).

  4. Sprint 151ptc newshipped

    The second gap closed: there was no way to start — fourteen commands and no scaffolding, so a first-timer guessed every convention (entry file, test placement, the manifest's strict shape). ptc new <name> writes an app — main.pt with a real function, a real test block, and the capability form in a comment — plus a README naming the four commands that matter; --lib writes a package instead: a pub module carrying the /// doc lines the publishing gate requires, a polytone.pkg in the exact order ptc pack validates, and its README. The scaffold runs, tests, and formats clean with no edits, and a package survives ptc pack all the way to its registry line. Module stems are hyphen-free — the only names legal both in a manifest and as an import identifier. The CI gate runs the promise itself (scaffold → test → run → fmt --check → pack) plus the guards: bad names, a leading flag, and an existing directory that is never overwritten. The guide and the IDE language card (v7) teach ptc new and the new call-stack output.

  5. Sprint 150Call stacksshipped

    Opens Phase M24, The programming experience (sprints 150–154) — owner-directed: what is still urgently missing for us and for users to actually program with the IDE and POLYTONE? The answer came from sitting down and trying, not from guessing: no call stack on a runtime error, no ptc new, and an IDE editor without code intelligence. This sprint ships the first: RuntimeError.frames is filled at the interpreter loop's four error exits and rendered under the message — each frame names its function and position, innermost first, callers at their call sites, synthetic frames suppressed. a() → b() → c() used to report only 'line 2, column 12'; it now prints the whole chain down to main. Both audiences win: a human debugging, and the pass@2e repair loop, which sees exactly what the human sees. Also: preflight now checks the fixture expectations — the full workspace suite revealed media_bridges.expected had drifted since the Sprint-145 stylesheet change, five sprints behind green local gates. And M23's F3.1 lands: the hemispheric ambient was inverted for box tops, sphere upper hemispheres and cylinder caps (the shipped winding gives them ny < 0), so it brightened undersides and darkened tops — the normal is now oriented toward the camera before its y is read.