Roadmap
POLYTONE grows in focused sprints. The timeline below is the real history — and the toolchain is green today.
1–30Phases M1–M3
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.
- ✓Sprint 1Lexer & spec v0.1shipped
Tokens, indentation blocks, the keyword surface, and the first written spec.
- ✓Sprint 2Parsershipped
Full expression and statement grammar with teaching parse errors.
- ✓Sprint 3Type systemshipped
Local inference, bidirectional checking, strictness rules (no truthiness, no implicit conversions).
- ✓Sprint 4VM executionshipped
Stack-machine IR (PTIR) and an interpreter with value semantics and checked arithmetic.
- ✓Sprint 5WASM scalar backendshipped
Int/Float/Bool + print compiled to WebAssembly, differentially tested against the VM.
- ✓Sprint 6Records & matchshipped
Nominal product types, canonical constructors, patterns with enforced exhaustiveness.
- ✓Sprint 7Standard preludeshipped
Built-in methods on core types; everything fallible returns an Option.
- ✓Sprint 8Enumsshipped
Sum types with qualified variants, payload matching, exhaustiveness over variants.
- ✓Sprint 9Function valuesshipped
Lambdas as unnamed declarations, by-value captures, map/filter/fold.
- ✓Sprint 10Test blocks & assertshipped
Tests as a language construct, comparison asserts that report both operand values, ptc test.
- ✓Sprint 11Modules & importsshipped
Multi-file programs: import loads a neighbor file, pub fn is the export, access is always qualified (mathx.double).
- ✓Sprint 12Type exportsshipped
pub record and pub enum cross module boundaries: qualified types (shapes.Point), variants, patterns, exhaustiveness — nominal identity program-wide.
- ✓Sprint 13Stdlib in POLYTONEshipped
ints, lists, texts — written in the language itself, self-tested with test blocks, resolved via the stdlib search path.
- ✓Sprint 14Bytes & file I/Oshipped
Binary buffers with value semantics, read_file/write_file as Results — and POLYTONE's first real image (binary PPM).
- ✓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).
- ✓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).
- ✓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).
- ✓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.
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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 & ecosystem
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.
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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 pass
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.
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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.0
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.
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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.0
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.
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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).
- ✓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 — POLYTONEide
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.
- ✓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.
- ✓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).
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 pass
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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).
- ✓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 — Launch
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.
- ✓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.
- ✓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.
- ✓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).
- 74Sprint 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 — Capabilities
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 pass
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.
- ✓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).
- ✓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.
- ✓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).
- ✓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.
- ✓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.
- ✓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.
- ✓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 — Batteries
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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).
- ✓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.
- ✓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-tuning
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.
- ✓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.
- ✓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).
- ✓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).
- ✓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).
- ✓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 decomposition
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 patterns
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.
- ✓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.
- ✓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.
- ✓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 audit
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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) — complete
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 audit
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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).
- ✓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.
- ✓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 audit
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.
- ✓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).
- ✓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).
- ✓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.
- ✓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.
- ✓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.
- ✓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 loop
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.
- ✓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).
- ✓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.
- ✓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).
- ✓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.
- ✓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.
- ✓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.
- ✓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 maturity
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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).
299–301Phase M46 — Go-live
Everything repo-side exists — the checkout page, the fulfilment sketch, the OAuth adapter, the release pipeline with the Pro-CLI archives, the LAUNCHED flag. These sprints execute owner decisions and external registrations: Paddle checkout live, the OAuth decision, the tag push and the launch commit (the Sprint-74 row). Listed so this page tells the truth about the distance to market.
- 301Sprint 301The launch commitnext up
The version tag pushed (Pro CLI and ptc archives on the release), LAUNCHED = true with its companions — the Sprint-74 row executes here. 0.x stays.
- 300Sprint 300OAuth: decidenext up
Either the Anthropic client registration lands and the placeholder values go, or the product settles on API keys plus local backends with the adapter documented as dormant. A decision, recorded either way.
- 299Sprint 299M46 opens — checkout livenext up
Paddle account and product, the fulfilment endpoint deployed with the signing key and webhook secret, the signature check real, CHECKOUT_URL pointing at a served page. Owner-gated.
293–298Phase M45 — The third full-codebase audit
Every phase through M18 was audited by M20; the M25–M40 surface has had only per-phase reviews — and the language core changed most there (methods, traits, bounds, tuple paths, hashed collections, multi-error reporting) without ever seeing the full eight-round protocol. The register, four fix-sprints, the record.
- 298Sprint 298M45 close — the true recordnext up
Disposition, phase complete, the audit rhythm's next slot noted.
- 297Sprint 297Fix-sprint R7/R8next up
The benchmark harness and its gates; the records.
- 296Sprint 296Fix-sprint R5/R6next up
IDE core (router, streaming, sessions, ledger); web tools and the shared runtime.
- 295Sprint 295Fix-sprint R3/R4next up
The stdlib incl. the v4/v5 media codecs and the z-buffer; the polytone and ptc CLIs.
- 294Sprint 294Fix-sprint R1/R2next up
Typeck methods, traits, bounds and monomorphization; VM, PTIR, codec v9 and the hashed collections — each finding with its regression test.
- 293Sprint 293M45 opens — the registernext up
The third full-codebase audit: eight rounds by area over the M25–M40 surface, parallel adversarial passes, every verdict by execution, S1s fixed ahead — M45-REVIEW.md.
287–292Phase M44 — The tool backlog
The M22 tool-UI pool comes due, one tool per sprint, format-first where a format extension is needed (additive, version-gated, byte-pinned for older versions): image editor, sound studio, 3D workshop, video editor + web viewer, playground, then review and close — the M22 disposition finally closed.
- ✓Sprint 292M44 review + closeshipped
Adversarial review by execution (editor mutations run against the codec parser), a proof session through every tool's codec, the M22 disposition finally closed.
- ✓Sprint 291The playgroundshipped
Examples browser, native-format tabs, cross-tool handoff, converter fidelity; the cheap M23 carries (media-format doc titles, the spans interleave).
- ✓Sprint 290Video editor + web viewershipped
Render caches (the editor stops re-rendering unchanged scenes), the .ptw model block (v5) and page memoization.
- ✓Sprint 289The 3D workshopshipped
.ptm v4 backface culling via Mesh.cullable (closed shapes only, version-gated, a byte-pinned no-op for v3), zoom/reset/size picker.
- ✓Sprint 288The sound studioshipped
Track lifecycle (add, remove, reorder — the chain follows), the wav cache (an unchanged document never re-renders), audition across the band's octave shifts.
- ✓Sprint 287M44 opens — the image editorshipped
Drag-to-draw for rect/circle/line/gradient/ellipse (preview over the last render, commit on release; a still press keeps the click forms), the document palette (the v2 palette: section read and written with the codec's rules, a picked swatch spells the op by name, Add/Remove edit the section, names resolved for draw_ops in color slots only), zoom 16×, the pixel grid overlay and an eraser preset. Gated by execution: a palette document renders byte-identically through the codec and through the editor's program. Recorded moot: the incremental-render item (Sprint 262 measured and reverted it). web 219.
275–286Phase M43 — POLY-OS: the operating system and the machine
Owner-directed (2026-09-18): a POLYTONE operating system with an emulator to boot it, rethought from the ground up, a closed system that leaves nothing to wish for — readers, mail, editors, a browser, a terminal that runs POLYTONE programs, media tools, settings, a manual. The OS is a pure state machine (boot/tick) written in POLYTONE whose state stays inside the VM; the POLY-Machine is the host loop, mirrored in the browser (/os/) and headless (ptc machine), so CI boots the OS, types into it and asserts on the frame. Every frame is a .pti display list (v6 brings the system font), every disk a text image, every request answered by a later event. Twelve sprints: the plan + font, the machine, the kernel, the emulator, files + editor, reader + browser, mail, programs, media, tools + settings, help + launcher, review + proof + close.
- ✓Sprint 286M43 REVIEW + PROOF + CLOSEshipped
Adversarial review by execution (hostile events, huge files, disk corruption, fuel per tick, request re-entrancy), the perf pass (tick cost, dirty frames), and the proof session: boot → write a .pt in the Editor → run it in the Terminal → mail the output to yourself → read it in Mail → open the manual in the Browser → save → reboot from the saved disk with everything intact, screenshot pinned. M43-REVIEW.md.
- ✓Sprint 285Help + Launcher + the polish passshipped
The manual complete under /docs, the launcher (Meta+Space: apps, files, commands), every keyboard shortcut, the contrast theme; the site's guide section, llms.txt and the skills reference teach POLY-OS; the download page exports a disk.
- ✓Sprint 284Tools + Settings + the lock screenshipped
Five apps: a Calculator with a recursive-descent evaluator (decimals, + - * / %, parentheses, hex results, a recalled history), a Calendar (the month grid from the clock, arrows across month ends, a day's note opened in the Editor and previewed beside the grid), Notes (one quick text saved on Ctrl+S, Escape or blur), Tasks (the open windows, the uptime, the disk's use; Delete ends a window through the new CloseWindow effect) and Settings (theme, clock 24h/12h, wallpaper, mail relay, a lock password stored as its sha256 — every change a SetSetting effect the kernel persists). The kernel keeps a settings map with every key, draws the wallpaper and the clock in the configured format, hands apps the window list and the settings, and locks: Alt+L, lock in the Terminal or a set password at boot show the lock screen, Enter checks the hash, Escape clears. Found by execution: Ctrl+L as the lock collided with the Browser's address field — the lock takes the OS keys only. Sessions pinned in the kernel and headless (42, November 2023, a 12h clock, a password through the lock screen, a window ended). os/ 28 modules, 96 tests.
- ✓Sprint 283Mediashipped
The machine renders, the OS shows: four render programs in one text each for both hosts (image with a whole-pixel scale, model with a camera line, one film frame, a song's WAV); ptc machine answers @render, validates @play, keeps @asset pictures and composites them into the screenshot; the browser answers on the runtime with the same programs, plays a data-URL WAV, decodes assets into the twin, which blits the image op clipped to its box. The Gallery asks for a picture on focus and draws it as an asset, orbits a model with the arrows and zooms with +/-, steps a film's frames, plays a song; Paint edits a .pti with six tools, a palette, two-click shapes, a prompted label, undo and save, every op rendered to fit; Music edits a .pta on the text buffer with play, check and save. Samples on the factory disk, a Media manual page, the three apps in the table. Sessions pinned in the kernel, headless (the screenshot carries the asset) and through the blob (the scene's pixels counted in the rasterized frame). os/ 23 modules, 87 tests; web 211.
- ✓Sprint 282Programs runshipped
@exec end to end on both hosts: the driver's exec_on_disk reads the disk image, makes the program's directory its module root with every disk file in the sandbox and the whole stdlib in memory, and answers run / --test (a ptc test report) / --check (every independent error) with ok, compile-error or runtime-error; ptc machine answers every @exec of a tick on the latest disk and delivers the reply with the next batch, the way a host does. The OS emits the factory disk on the first tick of a factory boot, so a host's answers read the image the OS runs on; the context carries the kernel's next request id, so an app that correlates replies — the Browser — uses the kernel's numbering (found by execution). Samples on the factory disk: sum.pt adds its arguments, counter.pt prints a page for the Browser; the manual gained Programs. The session pins run, test, check and a page app rendering Counter 3.
- ✓Sprint 281Mailshipped
The format: mail document (id/from/to/date/subject/read headers, a blank line, the body raw; parse with teaching errors, an exact round trip), the mailboxes under /mail/<user>/{inbox,sent,drafts,trash}, local addresses (name, name@poly) delivered at once into the recipient's inbox with a copy in sent, remote ones posted as JSON to the relay named in the settings, and the relay's answers filed into the inbox on sync. The Mail app: folders with unread counts, the list, reading (marks read through a WriteFile), compose with to/subject/body fields (Tab cycles, the body on the text buffer), Ctrl+Enter sends, Ctrl+S drafts, reply and forward quote the original, delete to trash, s syncs. The welcome mail and the four folders live on the factory disk. Sessions pinned: launcher → Mail → read the welcome mail → compose to yourself → send lands in sent and inbox, in polyos.pt and headless. os/ 20 modules, 80 tests.
- ✓Sprint 280Reader + Browser + the manualshipped
A layout engine turns web.render's typed blocks into rows for a column width (headings, wrapped paragraphs, bullets, code, rules, links, quotes, notes, tables, navs, media placeholders; plain and markdown-ish texts too); a page module holds address, title, rows, scroll and error, resolves links (absolute, http(s), poly://, relative against the page's directory) and draws. The Reader shows .ptw documents and texts from the disk, follows links, remembers the way back, hands http links to the Browser and other files to their app, e edits the source, F1 opens the manual. The Browser adds tabs, an address field (Ctrl+L selects it, typing replaces), history with back/forward, reload, bookmarks in /home/user/bookmarks.txt, directory listings, http(s) pages through a Net request (the reply lands in the tab that asked) and .pt?args apps through an Exec request whose printed output is the page. The manual lives under /docs on the factory disk (index with a nav, welcome, terminal, editor, files, reader, shortcuts), every page gated through the web codec; every launched window now receives a Focus event. Sessions pinned in polyos.pt and headless. Found for the review: ptc fmt refuses a // inside a string nested in an interpolation. os/ 18 modules, 71 tests.
- ✓Sprint 279Files + Editor + dialogsshipped
The text buffer (lines, cursor, selection, multi-line insert, newline keeping indentation, char/word/line/page moves, undo/redo snapshots, find wrapping, replace, a scroll origin that moves the least it must), inline dialogs (a prompt with a field, a y/n confirmation, outcomes Continue/Submit/Cancel), the Editor (line numbers, .pt syntax colors, selection, status bar; save and save-as, open, find/replace, undo/redo, select all, clipboard through effects, run and test through the machine with an output pane, Ctrl+Home/End, clicks and drags, the wheel) and Files (toolbar, breadcrumb, listing with sizes, preview, keys, every operation a dialog into an effect, duplicates refused). The app table opens editor <path> and files <dir>; every text document opens in the Editor. Sessions pinned: edit from the Terminal, Ctrl+S writes the disk, the launcher opens Files, Enter opens a file in the Editor — in polyos.pt and headless through ptc machine. os/ 14 modules, 58 tests.
- ✓Sprint 278The emulatorshipped
POLY-OS boots on the site: /os/ is the POLY-Machine in the browser. The rasterizer twin of images.pt (pixel, rect, line with widths, circle, ellipse, polyline, label — every algorithm the codec's, unknown ops reported never guessed) is pinned by a gate that renders a corpus AND the frame POLY-OS draws at boot AND a frame after a session through the real codec in the blob and through the twin: byte-identical. The page owns the host loop of formats/machine.md — keyboard (DOM keys to protocol lines, the space key by itself), mouse, a 100 ms tick with the wall clock, @frame through the twin, @disk persisted in the browser, @exec run on the same runtime (run/--test/--check from the disk image), clipboard, title, cursor, halt with a powered-off overlay; Reboot, Factory disk, Export/Load .ptd, 1×/2×, fullscreen. The OS accepts Alt as its command key (browsers keep Meta+Q/T and Ctrl+T); the embedded OS image is the repo's os/ byte for byte, gated. web 207.
- ✓Sprint 277The kernelshipped
os/ — ten modules, 38 tests, all canonical: the protocol codec (input lines to events, an Output with counted sections), the virtual disk with its format: disk codec (exact round trip, teaching errors), three themes and the metrics, the display-list builder (geometric clipping, labels cut to whole characters, widgets), the app kit (events, effects, context, steps), the Terminal (ls cd pwd cat mkdir touch rm mv cp echo tree df date whoami open edit run test check theme clear exit poweroff help, history, Tab completion, wrapped scrollback, every disk change an effect), the app table, the window manager (cascade, focus, drag, resize, menubar with launcher and clock, dock, notices), the factory disk, and the kernel entry boot/tick (settings via toml, global shortcuts, routing, effects applied, requests routed back by id, the disk emitted once, halt after the disk). ptc/tests/polyos.rs drives a whole session headless through ptc machine — ls, run + reply, a second terminal, tree, a saved file, power off — and pins the frames, the exec request, the disk image, the halt and a 640×400 screenshot; a saved daylight disk reboots. CI runs the OS self-tests and the formatting corpus over os/.
- ✓Sprint 276The machineshipped
formats/machine.md — the POLY-Machine protocol v1: the contract (pub fn boot(disk, machine) -> Os, pub fn tick(os, input) -> Tuple[Os, Text]), the input events, the output sections with line counts, the format: disk image. The VM calls a function with arguments and hands its return value back; the driver's machine session compiles once, checks the contract with teaching errors naming both signatures, keeps the state as an opaque value between ticks (print lines become @log, a failing tick leaves the state as it was, fuel per call). The browser runtime exports boot/tick/free with the sessions inside the wasm module — runtime.ts and polytone.mjs twins expose machineBoot; ptc machine boots headless, feeds an event script batch by batch, prints every section, renders the last frame through the real codec and writes the saved disk. The echo OS in tests/machine proves the loop on every surface: driver 7, web-rt 2, ptc 4 through the binary (a 640×400 screenshot with the label's pixels), web 4 through the blob, the shared runtime +1.
- ✓Sprint 275M43 opens — POLY-OS: the plan + the system fontshipped
Owner-directed: a POLYTONE operating system with an emulator to boot it, thought through from the ground up. The plan of record (.claude/plans/m43-poly-os.md): the OS is a pure state machine — boot(disk) and tick(os, input) — whose state stays inside the VM between ticks; the POLY-Machine is the host loop (browser and headless ptc machine, mirrored), every frame a .pti display list, every disk a text image, every request (net, exec, render, play) answered by a later event; twelve sprints from the machine protocol to a scripted proof session. Measured first: a 960-op frame costs under a millisecond per tick in the interpreter. Shipped with the plan: .pti v6 — label x y scale color words..., the 5×9 system font with every printable ASCII glyph (lowercase, punctuation, a replacement box), images.draw_label + label_width in both codec dispatches, version-gated, v1–v5 untouched; one ASCII-art source (scripts/font/system-font.txt) generates the codec's table and the emulator's TypeScript twin, a gate pins all three. The image editor round-trips label as a v6 document. M42 closed by record at 0.42.274 (M42-REVIEW.md); the tool backlog, the third full audit and the go-live shift behind the OS (M44–M46). images 37, web 193.
271–274Phase M42 — Traits travel
The spec §30.5 deferrals with an LLM prior, closed by design: every model expects an interface to be importable, and the spec refuses it at the declaration. pub trait across modules, the reach rule for imported bounded calls, the surface and card, an audit, the proof. Trait-side type parameters stay out (LLM-first decision).
- ✓Sprint 274The M42 audit — five confirmed by execution, five fixedshipped
Adversarial probes through the real toolchain over cross-module trait × generic type × Display in every nesting, the reach rule under nested instantiation and pub-bounded fn × pub trait: the 271/272 machinery held every probe; trait-side type parameters re-confirmed out. Found and fixed: S1 a generic type's mut self method never worked (since Sprint 159 — checker returned the template's receiver type, the monomorphizer never renamed the mutating call to its instance); S2 a re-exported type arrived with its fields only (methods 'declares no methods', trait impl 'does not implement', Display structural) — the reached module's whole surface travels now, and naming it without the import teaches the import; S3 the context slice dropped mut from every method receiver; S4 a doubled with block hid the trait-level error. Recorded by design: a generic record's Display cannot reach its element's Display (bounds live on functions only). Fixture relay_app.pt, typeck 318, differential 57/57.
- ✓Sprint 273The surface + card v17shipped
Card v17 teaches traits that travel (pub trait is module surface; with geometry.Shape: and [T: geometry.Shape], always qualified; a pub fn carries any nameable bound, never a private trait; any implementing type satisfies an imported bounded call; with-block methods need no pub) — the false 'module-internal' line retired, every claim a fixture. The context slice opens each impl block with a with Trait: member line (qualified in the importer's view), LSP hover covers traits with their full contract, IDE completion ranks traits with fns and never reads a with line as a variant, the guide gained a two-file example with recorded output, the Error Lab the private-trait refusal, the skill checklist the traveling rule. Measured, not shipped: the stdlib payoff — 0/48 corpus tasks call lists.largest/smallest, §27 + one-form hold, the payoff is user-side (Sprint 275's corpus tasks).
- ✓Sprint 272Reach rule II — any satisfier crosses the module boundaryshipped
The v1 reach rule (Sprint 228) admitted only scalar builtins as type arguments of an imported bounded call; the M36 audit had found its wrapper way out impossible. Retired: the exporter's instance is generated with the satisfier's own method as target — ranker.largest([Money(…)]) becomes largest[app.Money] inside ranker, calling app.Money.lt. The monomorphizer re-keys every type argument into the target module's view (an importer's Money is app.Money there; the exporter's Circle, spelled geometry.Circle by the importer, is bare at home), so same-named types never collide. A forwarded type parameter crosses, a pub fn bounded by a pub or imported trait travels (bounds qualified on export — only a private trait cannot bound a pub fn), a mut self trait method rewrites across the boundary, a three-module chain lands each instance where its template lives (a target naming a module the instance's module does not import is spelled as one dotted name and resolved globally by lowering), and an imported generic type's methods and Display resolve — that last one had been an ICE, found by execution in the sprint's own probe. The slice and hover spell an import's pub-trait bound as geometry.Shape. Spec §30.5/§30.6. Fixtures reach_app.pt + shapes_lib.pt, differential 56/56 first-run, typeck 316.
- ✓Sprint 271M42 opens — traits travel: pub trait + cross-module withshipped
A pub trait is part of its module's surface (spec §30.6): an importer's type implements it in with geometry.Shape:, a bound spells it [T: geometry.Shape] (also inside a multi-bound), and the exporter's own pub type satisfies that bound through the with block it exported. ModuleApi carries the pub traits with every signature type rewritten into the importer's view, so the contract is held exactly as a local one is, with the same teaching errors; an imported trait is keyed module.Trait, so a local Shape and an imported geometry.Shape are two traits. A traveling with block is callable surface: geometry.unit().area() and t.lt(u) on a time.Instant resolve for an importer without pub on each method. Teaching errors proven by execution (built-in with a prefix, module not imported, private trait, name not offered, local trait on an imported type, pub fn bounded by a user trait → reach rule II next). Static dispatch unchanged, no codec change. Fixture pair geometry.pt + traits_app.pt, differential 54/54 first-run, typeck 314.
265–270Phase M41 — The corpus grows: tier L
The instrument lost its headroom — pass@2e 36/36 twice, pass@1 33–34/36 — so the next teaching change cannot be measured. Tier L (multi-module, trait-bearing, media-composing tasks) restores the lever before the language grows: the shape, corpus 36 → 48, Baseline 10, hardening from the data, close.
- ✓Sprint 269The skills — POLYTONE for Claude Code and Qwen Codeshipped
Owner-directed: three agent skills in the Agent Skills layout both Claude Code and Qwen Code load from the same directory shape — polytone (the frozen language card the IDE and the Pro CLI send, the verify-and-repair loop, capabilities under mocks, the spec, the whole stdlib reference, the Error Lab, verified examples), polytone-media (the five formats with their full specifications and example documents) and polytone-toolchain (finding ptc, every subcommand, diagnostics as JSON, packages, polytone-mcp for both agents, the Pro CLI). scripts/build-skills.mjs generates every reference from the repo's truth (the card through the harness extractor — byte-identical, the one-card rule's fourth consumer; api.ts; errors.ts; spec, formats, mcp/lsp docs, examples verbatim) into a deterministic zip; the site deploy builds it into dl/skills/ (denied over HTTP) and get.php serves it through the existing password gate; the download page, llms.txt and the guide's agents section teach it. skills.test.ts pins frontmatter validity for both agents, every reference link, the card, every stdlib item, every diagnostic, every ptc subcommand and MCP tool, zip validity + determinism, and the gate/page/workflow triple. M41's close moves to 270; M42–M45 shift by one. web 187.
- ✓Sprint 270M41 close — the true recordshipped
Phase M41 complete. The benchmark page documents tier L — the multi-module judge (fence grammar, every file its own entry, modules first, hostile paths dropped, a missing file teaches) — and the comparability note across corpus sizes (runs 1–6 corpus 33, 7–9 corpus 36, 10+ corpus 48; a 48-run's 36-task line is what compares with 7–9: run 10 at 33/36 · 35/36, the run-8 level), plus the tier-L delta of 267–268. The voices: correction from the 268 cold residual lands in tune_mix's task text. m41-proof.test.ts derives the record's chart, run 10's headline, its tier-L line (10/12 · 12/12) and its 36-task subset from the published runs and pins the page's subset line to the same data. M41-REVIEW.md is the record: corpus 36 → 48, one judge path, 43/48 · 47/48, cold delta 3/5 → pass → 4/5, the first directory replays pinned, the skills inserted before the close. web 189.
- ✓Sprint 268Hardening from the tier-L datashipped
Two teaching errors rewritten from the run-10 first-attempt dataset: a partial variant pattern now shows the exact named form to write — the positional repair that was run 10's one residual cannot be reached from the message — and a method of the element type called on a list names the way to an element instead of listing the list's methods. The replay gate learned the multi-file form and lost a scratch-file race. Cold delta over the five run-10 failures: 3 of 5 pass cold (0 of 5 in run 10), both taught classes healed; the residual named the last gap — there was no way to write an arm that does nothing, and every candidate invented one. pass is now the empty statement; the same candidates re-judged under it read 4 of 5, the repair round takes the rest.
- ✓Sprint 267Baseline 10 — the first tier-L measurementshipped
The first measurement over 48: pass@1 43 of 48, pass@2e 47 of 48, median tokens-to-green 85,907 under card v16 — tier L 10 of 12 cold and 12 of 12 after one repair round; the 36-task S/M subset back at the run-8 level (33 · 35), the v16 lines holding cold. The audit is clean: one structured output per agent, no reads, no commands. Five first-attempt failures in five classes, four healed by their teaching error in one round; the residual is a variant pattern where the error named the rule but not the field-name form — the hardening candidate. The tier-L candidates are recorded verbatim as the first multi-file replays.
- ✓Sprint 266Corpus 40 → 48shipped
Eight more tier-L tasks, each two modules — two bounded generics instantiated across the seam, environment and clock composed under mocks, async probes batched over a mocked HTTP client, a video scene rendered and its frames counted, a v4 stereo score inspected channel by channel, a model of n boxes tessellated, a TOML manifest checked against the registry's publishing rules, a Display row type spelled from CSV fields. With the four of Sprint 265 the tier-L set is twelve across the surface; every reference is green through the exact judge path, and two corrections found by running them landed in the task texts. Corpus 48.
- ✓Sprint 265M41 opens — Tier L, the shapeshipped
A tier-L task is multi-module: the reference set lives in solution/, the hidden judge is one tests file per module, and the model answers in the IDE loop's own fence grammar — one path-labelled block per file. One shared corpus module judges for the runner and the harness alike: every candidate file verifies as its own entry, modules first, a missing file teaches before the compiler runs. Four reference tasks — a pub record with Ord and Display across the seam, a parser module under a mocked file system, a .pti scene embedded into a .ptw page, a pub bounded generic instantiated at two types — green through the exact judge path. The coverage gate widens by tier; the corpus grows from 36 to 40.
252–264Phase M40 — The second rotation
The open-ended improvement loop returns (the M34 precedent): loose ends, baselines, audits — whatever the numbers ask for next. Shipped: one card across three surfaces, Baseline 8, card v15, the CLI retries and the Pro-CLI distribution, the v4 audit, the chromatic grid. Planned close (Sprint 259): Baseline 9, one perf and one audit rotation over M40's own surfaces, the record at 264.
- ✓Sprint 264M40 close — the true recordshipped
polytone card prints the embedded card byte-exact, and the proof fetches the card from all three surfaces the way each really produces it — the built binary, the harness extraction, the IDE's card.ts under Node — and pins the bytes. M40-REVIEW.md is the record: pass@1 21 → 30 → 30 → 30 → 30 → 33 → 34 → 33 → 27, run 9 published as it fell and turned into the v16 cold delta (8 of 9), −31 % stereo render, 3× editor render, two audit rotations without an S1. Phase M40 complete.
- ✓Sprint 263Audit rotation III — M40's own surfacesshipped
Review by execution over sprints 252–262: six findings, four fixed. The studio accepted e#4/b#3/b#7 that the audio codec refuses — the note grammar now resolves through the codec's own twelve pitch classes, pinned by running both. The harness card extraction truncated at an escaped backtick and, like the CLI's, left unknown escapes raw where the IDE evaluates them — both extractors now mirror each other and refuse what they cannot evaluate. The IDE's double-spend twin wart from M38 is closed: cheap == strong is one tier. Confirmed clean: the distribution drift gate turns red on a renamed platform; retry parity on statusless errors.
- ✓Sprint 262Perf rotation III — one pass, one slice, one refutationshipped
audio.render_stereo synthesizes once instead of twice, bytes identical (sha256-pinned on two v4 scores, mono untouched): a 42-second five-voice score in stereo 42.8 s → 29.7 s (−31 %). The PPM bridge stops pushing bytes one at a time: the image editor's full render at 512x384 falls from about 250 ms to 84 ms, every pinned tool session unchanged. Recorded honestly: the incremental editor render (M22 F1.9) was built, measured and refuted — 257 vs 250 ms per op; the ops were never the cost. Reverted unshipped.
- ✓Sprint 261Card v16 — the cold deltashipped
The four run-9 classes, each line verified against the compiler before it was written: match is a statement, never an expression; every variant pattern is qualified, local enums included; there is no + on Text; .to_text() exists only on Bytes. The delta: the nine run-9 failures re-ran cold under v16 — 8 of 9 pass on the first attempt (0 of 9 under v15). The residual is a stdlib field name the card cannot carry; the teaching error already names it. One card, three consumers, byte-identical.
- ✓Sprint 260Baseline 9 — the card's own misdirectionsshipped
The second run under the evaluated card (v15): pass@1 27/36 · pass@2e 35/36 · tokens-to-green median 82 368. Expected 36/36 cold, measured the opposite — published as it fell. Nine attempt-1 failures in four classes, three of which the card teaches wrongly or not at all: match used as an expression, + on Text (run 7's class, four times now), local variants matched unqualified, .to_text() listed under common methods where Int has none. Eight healed in one round; the residual is the settled stacked-error class inside a broken match arm. Card-v16 candidates recorded for the next sprint. The harness recipe became code (benchmarks/gen/harness/).
- ✓Sprint 259The plan + the roadmap gateshipped
The whole project checked and planned: preflight green on 258, every open record consolidated (the M22/M23 tool pool, spec §30.5, the perf notes, the audit debt, the owner-gated externals) — M40 closes at 263, then M41 Tier L, M42 Traits reisen, M43 the tool backlog, M44 the third full audit, M45 go-live. Found and fixed on the way: this page renders sprints only through phase ranges, and 36 sprints (148–149, 200–217, 227–240, 257–258) had fallen outside every range — invisible here. Ranges corrected, the M23 branch added, and a gate pins every sprint to exactly one phase. web 170.
- ✓Sprint 258The chromatic gridshipped
The grid-design question recorded since M22 R2 and Sprint 237, settled: the studio's eight fixed rows become a chromatic band of pitch indices over the codec's whole note grammar (a–g, optional #, octave 1–7 — noteIndex mirrors audio.pt exactly, e#4 keeps its pitch under the canonical name). ▼/▲ shift the band by octaves; fitBand widens it on every refresh so a note in use is never hidden; sharps read as black keys; the band scrolls inside the panel; the audition derives equal temperament from the same index. The studio finally opens any document its codec renders. web 169.
- ✓Sprint 257The v4 audit — eleven confirmed, nine fixedshipped
The M40 audit rotation over the never-audited v4 surfaces (.pta pan/echo/stereo, .ptv wipe/also/scale) and their tool mirrors, every verdict by execution: two S2 (the video editor stranded also-move riders on delete/reorder and showed/exported a doc its own codec refuses — the M23 F2.4 class; the editor refused codec-legal clause order), four S3 (viewer/playground/video-editor soundtrack rendered v4 scores mono; the studio accepted numerals the codec refuses; a 1-px-axis wipe stood still; a v5 op in a scene named an unbumpable header), five S4. Timeline operations move whole units and sweep orphans; clauses are read token-wise; repeated clauses teach on both surfaces; the stereo bridge follows the version-4 header everywhere. Recorded: the coherent drum echo tap, render_stereo's double synthesis. video.pt 18, web 166, differential 51/51.
- ✓Sprint 256The distribution — the Pro CLI shipsshipped
The recorded 'binary distribution (extern)' item, repo-side complete: every version tag now also builds the Pro CLI and packs it into its own polytone-cli-<platform> archive per platform, checksummed next to the free toolchain. The distribution model is the IDE's own — the archive is served, the license is the gate: the binary is fail-closed without an offline-verified PTPRO key. The server gate allow-lists the new names, the download and /cli/ pages teach the rule, and a three-source drift gate executes get.php's own allow-list against the workflow's archive names and the rendered page. The owner's external step shrinks to pushing a version tag. Found and fixed on the way: a type error committed with run 8's benchmark rows had frozen every site deploy since Sprint 253 (tests don't type-check; the deploy build does) — the field is gone and preflight now runs the deploy build's own type gate locally.
- ✓Sprint 255The CLI retries — withRetry parityshipped
The IDE's retry policy, mirrored verbatim and hooked where the IDE hooks it: 429/5xx backs off (500 ms doubling, 8 s cap, three attempts) below routing and the ledger; a real refusal passes through. Proven against a live server through real curl: 429-then-200 lands, the M39 HTML-502 dead gateway heals, a 401 sends no second request. A parity gate pins the policy across both surfaces.
- ✓Sprint 254Card v15 — the media header lineshipped
The class runs 7 and 8 both measured gets its card line: every media document begins 'format: <kind>' then 'version: N'. Delta measured cold: both run-8 .ptv failures pass on attempt 1 under v15. Two runs of evidence behind, a cold-pass delta in front.
- ✓Sprint 253Baseline 8 — 33/36 · 36/36 under the evaluated cardshipped
The evaluated-card series opens: pass@1 33/36, pass@2e 36/36 (fully green the second time at 36), median 68,924 tokens-to-green. All three failures healed in one round; the .ptv header class recurred from run 7 — the card-v15 candidate hardens. The cleanest audit of the eight runs: one structured-output call per agent, nothing else.
- ✓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 stream
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.
- ✓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.
- ✓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.
- ✓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, sessions
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 CLI
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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–240Phase M35 — Workshop shine, then the first rotation
Owner-directed: the tools deserve to look as good as they work — render quality, layout fit, and one wow feature per tool (218–226). Sprints 227–240 then ran the perpetual loop under the M35 version line: baselines 4–7 (the corpus fully green three times, then 33/33 cold), pub bounded functions (the M36 plan) with their audit, card v13/v14, round spheres and the z-buffer, the .pta v4 and .ptv v4 format revisions, and the corpus at 36.
- ✓Sprint 240Baseline 7 — 34/36 · 36/36shipped
First measurement over the grown corpus: pass@1 34/36, pass@2e 36/36 after one repair round; two of the three new v4 tasks passed cold. Both failures were one taught class each, healed in one shot. Seven runs chart 21 → 34, every step attributable. Seventh published run; the audit stayed clean.
- ✓Sprint 239The corpus learns the v4 surfacesshipped
Corpus 33 → 36: stereo_field (render_stereo + pan law), wipe_reveal (build + probe a v4 wipe), rider_film (also-move rider) — every reference first-run green through the exact judge path. The coverage gate learned history: the newest run covers the full corpus, older runs keep the corpus of their day.
- ✓Sprint 238.ptv v4 — wipes, riders, scaleshipped
wipe slides a directional reveal between scenes, also-move rides the move above it (simultaneous tweens over one scene), and scale stretches a sprite across its tween — version-gated, keyword-led clauses. The rider re-renders its span so the old move path is untouched; the byte pin proves v1–v3 unchanged. The editor speaks all of it.
- ✓Sprint 237The studio's v4 controlsshipped
Pan slider and echo pair on every track chip (decay appears once echo is on, seeded codec-legal), riding the standard undo path; the grid's audition pans with the active track. The chromatic grid stays recorded as a grid-design question.
- ✓Sprint 236.pta v4 — pan, echo, stereoshipped
The score learns space: pan and echo as order-free voice clauses (version-gated), echo as event replication so the optimized synth is untouched, stereo as additive API (render_stereo + two-channel WAV). A centered score's channels equal the mono render exactly; the byte pin proves v1–v3 unchanged. The studio round-trips and plays v4 in stereo.
- ✓Sprint 235The z-buffer auditshipped
Verdicts by execution: giant-coordinate models at minimum zoom overflowed the fill's integer edge products and crashed — on the new path AND inside the old painter's draw_polygon. All five raster sites now clamp screen coordinates to ±1e9; byte-pinned hashes prove sane scenes are untouched. Stale painter docs swept.
- ✓Sprint 234The z-buffershipped
The painter's whole-triangle sort failed structurally — every path now depth-tests per pixel, perspective-correct: overlapping surfaces are gone categorically, orbit and turntable clean, shadows correct. A tenth of a second more per fine render — the price of correctness.
- ✓Sprint 233Baseline 6 — 33/33 coldshipped
The corpus fully green on attempt one for the first time, zero repairs — the membership line healed the last residual as predicted. Six runs chart twenty-one to thirty-three, every step attributable. The sixth published run.
- ✓Sprint 232Round spheresshipped
The showcase smooth-shades via vertex normals — the maturity-pool item carried since the format era, and the exact faceting in the owner's screenshots. No measurable cost; the byte-pinned classic paths unchanged.
- ✓Sprint 231Baseline 5 + card v14shipped
Fully green for the third time; the four-run residual is one spelling — contains on a Map — and card v14 adds the membership line the data demands. The fifth published run.
- ✓Sprint 230The M36 audit — six confirmed, six fixedshipped
An imported generic function as a value was check-green but panicked the compiler and bypassed both new gates — the mirrored guard teaches instead. The reach-rule advice named an impossible fix, executed to prove it; it now names working ways out, forwarding gets its own error, and hover prints bounds.
- ✓Sprint 229The surface teaches the new capabilityshipped
Doc and context-slice signatures print generic bounds — the missing bound invited exactly the refused call — and card v13 carries the pub-bounded line. The stdlib payoff was declined with its reason recorded: widening existing functions would break the compatibility promise.
- ✓Sprint 228Pub bounded functions — built-in bounds travelshipped
Bounds ride the exported signature and importers enforce them at the call site; scalar satisfiers reach the exporter-side synthesized methods, records teach the reach rule with the wrapper way out, user-trait bounds still refuse. Differential fixtures across the boundary, first run green.
- ✓Sprint 227Baseline 4 — fully green againshipped
pass at one thirty of thirty-three, the repair round all thirty-three — fully green for the second time. The nested stacked-error task repaired in one round: the repair prompt teaches that a seen class generalizes, and the model applied it where collection cannot reach. The fourth published run.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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–217Phase M34 — The token economy
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. The first full measure→teach→measure cycle closed here (pass@1 21 → 30 of 33, the corpus fully green); its loop continues inside the rotations (M35's tail, M40).
- ✓Sprint 217The M34 audit — two confirmed, two fixedshipped
The phase audited itself, every verdict by execution: a failed for-pattern leaked its scope frame — under multi-error a top-level function drew a fabricated nested-function error and sibling-arm extras were suppressed — and a mutating method on an indexed receiver evaluated an impure index twice, corrupting across elements and desynchronizing the generator. Both fixed, with executed repros as regressions and a first-run differential fixture.
- ✓Sprint 216Sibling match arms report independentlyshipped
Arm patterns bind arm-locally, so the walk continues past a failed arm and collects the siblings' errors, first error byte-identical, exhaustiveness skipped over broken arms. The nested class — a second error inside the broken arm's body — is honestly settled as unreachable without full pattern recovery, out of proportion for the win.
- ✓Sprint 215The judge speaks every error; an honest correctionshipped
ptc test and run print every independent declaration-level error before aborting, with the first line byte-identical — multi-error now reaches the repair loop. Corrected honestly: the real benchmark case holds both errors in one function, which per-declaration isolation does not split; the precise next candidate is match-arm pattern-error collection.
- ✓Sprint 214One check, every independent errorshipped
Up to five independent declaration-level errors per check — declarations are independent by construction, the first error stays byte-identical, and the diagnostics array was the contract from day one. The language server publishes them all; the CI proof pins the motivating benchmark failure: both stacked errors in one check, where the second once cost a whole extra attempt.
- ✓Sprint 213Intent blocks: settled measured-not-nowshipped
The understand pass ended in the deciding measurement: tokens-to-green is the fixed per-attempt cost times attempts — boilerplate reduction cannot move it; the lever is first-attempt correctness. The keyword stays reserved, the spec records the verdict, the examples-derive-tests sub-idea is filed for re-opening. Effort goes to multi-error reporting, where each surfaced error can save a whole attempt.
- ✓Sprint 212The envelope plateaushipped
Inside the settled envelope window every factor is exactly one or settled, and IEEE arithmetic keeps the constant bit-exact — one compare instead of two to three divisions per plateau sample. Audio render another sixteen percent faster, thirty cumulative, byte-identical including the envelope edge cases.
- ✓Sprint 211Baseline 3 — the tuple decision validatedshipped
Thirty of thirty-three pass attempt one, thirty-two the repair round. The pixel probe passes cold via exactly the tuple shape both earlier baselines guessed. A new candidate from the run: stacked same-class errors surface one per repair round — multi-error reporting recorded. The third published run.
- ✓Sprint 210get_pixel speaks the models' priorshipped
The pixel probe returns a tuple: both baselines independently destructured r, g, b — the pattern form is the prior, measured in two attempt-one failures. One-move migration across stdlib asserts, examples, the benchmark reference, guide, and the regenerated API reference; every gate green first-run.
- ✓Sprint 209The audio synthesis pass + the move memoshipped
Per-sample text dispatch became a per-event integer with the math inlined verbatim; min and clamp inlined branch for branch, invariant conversions hoisted, the accumulator prefilled in chunks — audio render seventeen percent faster, byte-identical including the envelope edge cases. The video move arm memoizes per-offset frames: twenty-two percent faster, byte-identical. The plateau-segmentation candidate stays recorded, not rushed.
- ✓Sprint 208List.slice + the y4m frame dedupshipped
The strict half-open slice contract now lives on all three sequence types, as one host-level copy. The y4m export deduplicates repeated frames with it — native list equality is one opcode — and the replay film's whole pipeline fell from 28 seconds to 0.3, byte-identical. Differential 48 first-run, card v12; a false lead (the bench's own interpreted sha256) recorded honestly.
- ✓Sprint 207List.extend — a frame append is one copyshipped
The third mutating method: a bulk append as one host-level copy, riding the in-place machinery — extending a list with itself keeps the copying path. No encoding change, a first-run differential fixture, card v11. The video pipeline cashes it: a replay film renders nine times faster, byte-identical; the honest residue names the y4m per-pixel math as the next candidate.
- ✓Sprint 206A film rasterizes each scene onceshipped
The scene cache removes a full rasterization pass per replayed timeline entry, lazily, so error behavior is untouched and every output byte-identical. The honest measurement names the next target: frame assembly is one interpreted push per integer — a bulk extend method will make it a single host-level copy.
- ✓Sprint 205The flood fill sheds its quadratic stackshipped
The fill's pop copied the whole work stack per visited pixel — quadratic in canvas area; a forty-fill document never finished. Cursor-based pops in both implementations render the former-timeout case in three seconds, at least a hundredfold faster, byte-identical with every reference hash unchanged. Found by the sprint's own benchmark sizing.
- ✓Sprint 204Baseline 2 — the corpus fully greenshipped
The same harness against the improved toolchain: pass@1 rose from 21 to 30 of 33, pass@2e from 30 to 33 — fully green for the first time. Mean tokens-to-green fell 16 percent, the run total 20, because fewer repairs were needed; one failure repaired on exactly the new Map-synonym error. The second published run; the measure-teach-measure loop closed its first cycle.
- ✓Sprint 203The CoW-defeat passshipped
The polygon painter, gradient, and flood fill delegated through a field assignment that cloned the frame buffer per call — one clone per triangle on the mesh path. Direct field writes make a 1166-triangle fine render 13× faster, byte-identical and sha256-pinned; the y4m emitter went single-pass; method near-misses teach the spelling first. One refuted candidate reverted: measurement beats estimation.
- ✓Sprint 202The allocation passshipped
Nominal names moved behind reference counts — a record clone was a malloc, an enum clone two; now both are bumps. Text literals are pre-wrapped at decode time, the wire format untouched, and text indexing walks the length only on the error path. Measured: a record-and-enum loop dropped 30 percent, a literal loop 11. Sixty-two suites, the 46-fixture differential, and the blob budgets all green; the remaining scout candidates are recorded for the next pass.
- ✓Sprint 201Card v10 — taught from the failure datasetshipped
The attempt-1 failure classes became card lines — two were pure card gaps: trim() and map index assignment existed but went unmentioned. Measured twice on the twelve v9 failures: six passed fresh, one empty-literal line healed exactly the four remaining inference deaths — ten of twelve now pass attempt one cold, a projected corpus pass@1 of about 31 of 33. The residue names the next targets.
- ✓Sprint 200Errors name the fix firstshipped
The baseline's never-green class closed: a bare Ok, Err, or Some statement in a Result or Option function now teaches write 'return Ok(...)', and a discarded value matching the declared return type teaches write 'return' in front of it. The delta is measured: the recorded candidates re-entered the repair round and went three of three green; both messages are pinned as CI replays beside the Sprint-138 set.
- ✓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.
- ✓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 viewer
The arc's final tool: the shared tool shell, a rendered .ptm path, tabs/history/bookmarks, and a site editor with live preview.
- ✓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.
- ✓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.
- ✓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.
- ✓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 editor
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 studio
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 workshop
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 workshop
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 & playground
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 pass
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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: traits
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 types
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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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.
- ✓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 experience
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.
- ✓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.
- ✓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.
- ✓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).
- ✓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.
- ✓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.
148–149Phase M23 — Supervised fine-tuning: the M21+M22 audit
Owner-directed: look for issues and check and refine all logics. M21 (the benchmark infrastructure) and M22 (five format versions in seven sprints) were the only never-audited phases. Sprint 148 opened the six-round register with five S1s fixed ahead; 149 cleared rounds 1 and 2. The planned fix-sprints 150–152 were taken by M24 (the programming experience), and the rest of the register closed in M27's Sprint 169 — the M23-REVIEW.md disposition records the whole path.
- ✓Sprint 149The R1/R2 backlogshipped
Clears the images/audio rounds. F2.5: the studio now reads and writes the whole .pta v3 surface — it accepted version: 3 but none of its content (master: fell into the header else-branch, song: p x3 failed chain validation), so a codec-valid v3 document could not be imported and toPta(parsePta(doc)) was lossy for every v3 file; master joined the session (emitted only below 100, lifting the document to v3 like a drum wave) and the import expands xN repeats with the codec's exact rule — declared names win, the token is strictly x<digits>, and a repeat in a v2 document teaches the bump. F1.4: draw_ops silently accepted brighten 9999 where the renderer errors — the two dispatches now agree on validity. F1.5/F2.6: the missing coverage — a zero-radius outline paints nothing, and the studio/drums seam is pinned (a v3 document with master, a drum track, and song: beat x3 round-trips stably; a drum wave in a v2 document is rejected as the codec rejects it). F1.6: the even-width line's one-pixel parity bias documented. Images 32, web 101.
- ✓Sprint 148The review record + the S1 remediationshipped
Opens Phase M23 (the M21+M22 audit, sprints 148–152) — owner-directed: look for issues and refine all logics. The pick follows the project's rhythm: M21 (the benchmark infrastructure) and M22 (five format versions in seven sprints) were the only never-audited phases. M23-REVIEW.md holds six rounds and ~31 findings, with five confirmed S1s fixed ahead. F1.1: the ellipse outline was a filled lens at unequal radii — the ring test inset both radii by 1, which is no inset when rx != ry, and rx == 1 skipped the inner test entirely, so the outline was the filled shape; replaced by an exact boundary test. F2.1+F2.2: the xN song repeat shadowed pattern names (a document declaring pattern x2: could never play it) and replace('x','') stripped every x, turning xx4 into a repeat and silently dropping x1x; a declared name now wins and the token is strictly x<digits>. F2.4: the studio exported drums in a version: 2 document its own codec rejects; the document now declares the version its content needs, and the importer gates drum waves like the codec. F5.1: nav: addresses were never validated while link addresses are, so item javascript:alert(1) reached exported standalone HTML as a live link; nav now carries the link rule. Plus five cheap fixes. Regression tests: images 31, audio 18, web 16.