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.
- ✓Sprint 298M45 close — the true recordshipped
Disposition, phase complete, the audit rhythm's next slot noted.
- ✓Sprint 297Fix-sprint R7/R8shipped
The benchmark harness and its gates; the records.
- ✓Sprint 296Fix-sprint R5/R6shipped
IDE core ↔ CLI parity (ledger, PLAN, stream usage, retry); the emulator's exec sandbox, comment-first documents, studio pattern lengths, converter links, the rasterizer twin's grammar and clipping, polytone.mjs edges.
- ✓Sprint 295Fix-sprint R3/R4shipped
The stdlib incl. the v4/v5 media codecs and the z-buffer; the polytone and ptc CLIs.
- ✓Sprint 294Fix-sprint R1/R2shipped
Typeck methods, traits, bounds and monomorphization; VM, PTIR, codec v9 and the hashed collections — each finding with its regression test.
- ✓Sprint 293M45 opens — the registershipped
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.
302–307Phase M47 — The third rotation
Opened after the M45 close while M46 waits on the owner: the loop's phase types run over the surface that grew since M40 — the plan and the go-live checklist (302), perf rotation IV (303), the audit's recorded remainders (304), POLY-OS used through the real browser (305), corpus tasks and card candidates for the unmeasured surfaces (306), review and close (307). M46 stays owner-gated and untouched.
- ✓Sprint 307M47 review + closeshipped
Review by execution over 302–306, M47-REVIEW.md, the true record; next stays M46 (owner-gated) and the fourth rotation.
- ✓Sprint 306Corpus + card readinessshipped
Tier-L tasks for the surfaces no baseline has measured (a traveling trait, .ptm v4 cullable, .ptw v5 model, .pti v6 label); card v18 candidates recorded, unmeasured until an owner baseline run.
- ✓Sprint 305The use testshipped
POLY-OS used through the real browser as a user would — every app, shortcut and dialog; findings by execution, fixed, pinned in .events sessions.
- ✓Sprint 304The remaindersshipped
The M45 recorded items executed one by one: fixed where a fix is a line form or a guard, re-recorded with the reason otherwise.
- ✓Sprint 303Perf rotation IVshipped
Measure first: POLY-OS boot, tick and frame raster, ptc machine throughput, the web blob's budget headroom, the site build; ship the top item measured, byte-identical where pixels are involved.
- ✓Sprint 302M47 opens — the plan + go-live readinessshipped
The plan of record and docs/GO-LIVE-CHECKLIST.md: the owner's exact steps for 299–301 with everything the repository already verifies by execution; one stale path fixed while verifying.
496–499Phase M96 — The installers
POLY-OS as packages for macOS, Linux and Windows and as a server package — built, self-tested and checksummed; publishing stays the owner's decision.
- ✓Sprint 496M96 OPENS: POLY-OS as an app you installshipped
POLY-OS can now be packaged for installation with a single command. On a Mac it builds POLY-OS.app in a disk image. Linux gets an archive with a launcher entry and a .deb, and Windows a zip. The app icon is itself drawn in POLYTONE and rendered by the toolchain: a rail and two tiles, the new design in miniature. Before a package counts as finished, its own program starts POLY-OS without a window and checks that the OS appears. Each package comes with checksums. Nothing has been published yet: releasing it is the owner's decision.
492–495Phase M95 — The new design
A stage instead of a desktop: tiles instead of overlapping windows, a rail of pictograms, one palette for everything, a calmer visual language.
- ✓Sprint 492M95 OPENS: POLY-OS gets a stage instead of a desktopshipped
POLY-OS has a completely new design. Instead of overlapping windows with title bars and a dock of four-letter buttons, the app in front fills the screen, or two share it side by side (Alt+S). A slim rail on the left shows the open apps as small pictures. A click or Alt+1 to Alt+9 brings one forward. At the rail's foot sits the palette, also on Alt+Space: type the name of an app or a file and Enter opens it. A status line at the top says where you are. Surfaces have rounded corners and one warm accent colour. There are three new themes, ink, paper and contrast, and settings from before are carried over.
- ✓Sprint 493POLY-OS: the palette also runs actionsshipped
The palette now finds not only apps and files but also the system's own actions. Type 'paper' and Enter switches to the light theme, 'split' shows two apps side by side, 'lock' locks the screen, 'close' closes the app in front and 'power off' shuts down. What used to hide in menus is now one typed word away.
- ✓Sprint 494POLY-OS: every app in the new styleshipped
The building blocks that every POLY-OS app is made of now follow the new design. Buttons are rounded chips, and the active one carries the accent colour. Text fields are rounded. A selected row has an accent bar, scrollbars are slim and appear only when there is more to see, and every app's status line is a quiet footer under a hairline. All fourteen apps changed with them, and Mail now shows only the folders that fit in a narrow tile.
- ✓Sprint 495M95 CLOSES: every app in every theme, reviewed on real framesshipped
The new design of POLY-OS was checked on real screen images. All fifteen apps were opened in all three themes, ink, paper and contrast, and all of them follow the same language: tiles on the stage, the rail of pictograms, rounded chips, a quiet footer, one accent colour. The new guide docs/POLYOS-DESIGN.md explains the design and shows the contact sheets. Next comes M96: installers for macOS, Windows and Linux, and a package for servers.
488–491Phase M94 — Security
Security as a whole: isolated jobs, the clipboard gesture, HTTPS in the server, network access only with the user's permission, a written threat model.
- ✓Sprint 488M94 OPENS: programs run in their own process; the clipboard needs a gestureshipped
Every program and render that POLY-OS starts now runs in its own child process. If one crashes, runs out of memory or hangs for two minutes, only that process ends, and the request answers with an error. The desktop app and the server keep running. On Linux each job is also limited to 2 GB of memory. The OS can use the clipboard only within two seconds of a key press or click, so it can never read or overwrite it unnoticed. The new document docs/POLYOS-SECURITY.md lists every threat to POLY-OS, its countermeasure and the test that proves it.
- ✓Sprint 489POLY-OS server: HTTPS built inshipped
polyos serve now speaks HTTPS itself, so passwords and screens stay private on the network even without a proxy in front. You can give your own certificate, for example from Let's Encrypt. Alternatively the server makes a self-signed one once and prints its fingerprint, which you compare with the one your browser shows. Over HTTPS the login cookie is only ever sent encrypted. Plain HTTP to the HTTPS port gets nothing.
- ✓Sprint 490POLY-OS: the internet, with the user's permissionshipped
The browser and mail apps in POLY-OS can now reach the internet on the desktop and on the server, but only the host names the user has listed in /etc/net.allow on their own disk. A program cannot add itself to that list. Only HTTPS is used, an address in a private network is never contacted (not even through a redirect), and answers are limited to 8 MB in 15 seconds. A request that is not sent tells the app why, and the browser shows it, for example 'example.org is not allowed - add the line example.org to /etc/net.allow'. The browser version of POLY-OS makes no network requests and says so.
- ✓Sprint 491M94 CLOSES: every security claim checked and trueshipped
A security review checked every claim in POLY-OS's threat model against the code. Two did not hold, and both are fixed. On macOS, the memory limit for programs is now enforced too, because the host watches each program's memory use and stops it at the limit. The 15-second limit on network requests now covers every single step, so a server that answers very slowly can no longer tie up the system. In addition, POLY-OS now blocks every IPv6 address form that leads into a private network. Only a person at the keyboard can change the network allowlist, never a program and never a bug in the OS.
486–487Phase M93 — The server
polyos serve: every user's own POLY-OS in the browser, pixels out and input in, with accounts, revocation and limits.
- ✓Sprint 486M93 OPENS: POLY-OS as a server — every user's own POLY-OS in the browsershipped
polyos serve runs POLY-OS for many users. Each logs in from a browser and gets their own operating system, with their own disk on the server. The browser receives only pictures of the screen and sends back keys, mouse and clipboard, so no OS code or data ever reaches it. polyos user adds, changes and removes accounts. Passwords are stored as Argon2id hashes and never pass the command line. A change takes effect at once, even on a running server and on screens already open. Logins have a brake that parallel guessing cannot get past, and the server checks the host name and every request's origin. It refuses any input that is not a key, mouse, wheel or paste line, and limits connections, time, memory and disk space per user.
- ✓Sprint 487M93 CLOSES: the server, reviewed before it shippedshipped
The record of the server phase lists what a security review found in the server code and how each finding was fixed. All of this happened before the code entered the repository. Almost every finding concerned something that outlived its permission: a login after its user was removed, an open screen after a logout, a session after it was switched off. Each now has an owner that ends it. Next comes M94, security as a whole: encryption in transit and at rest, a memory limit for programs, and network access only with permission.
482–485Phase M92 — The native machine
POLY-OS as a desktop app on macOS, Windows and Linux: a native window, a Rust rasterizer pinned to the codec, the disk in the user's data folder.
- ✓Sprint 482M92 OPENS: POLY-OS as a desktop appshipped
POLY-OS leaves the browser. The new polyos program opens a window on macOS, Windows or Linux and boots the operating system written in POLYTONE inside it, with the OS and the standard library built in. Keys, mouse, wheel and clipboard reach the OS as they do in the browser. The 640×400 screen scales in whole steps, so pixels stay sharp. The disk is saved in the user's own data folder, atomically and readable only by that user. Programs and renders started from the OS run in the background, so the window never freezes. A new Rust rasterizer draws every frame, and tests check it pixel for pixel against the image codec on real OS sessions.
- ✓Sprint 483POLY-OS desktop: a run without a window, and its manualshipped
polyos --events types a script into the OS without opening a window, and --screenshot writes the picture the app itself drew. An installed POLY-OS can now prove on any machine that it boots and works, display or not, and nobody's screen is captured to see what it shows. The new guide docs/POLYOS-DESKTOP.md describes the desktop app: how the screen scales, which keys reach the OS, where the disk lives, how the clock and time zone are read, and what is still missing (sound, and the network, which waits for the permission model).
- ✓Sprint 484POLY-OS desktop: the host survives the guestshipped
A review of the desktop app found twelve problems, and all but one are fixed. A crafted disk image could make a background job crash and leave the app waiting forever; now every job answers, even one that fails. One error in an OS app no longer closes the window; the title shows it and the app goes on. A scripted run no longer touches the user's saved disk. Input is limited to the screen's pace, background jobs and pictures have caps, and a failed save is shown and retried. On German keyboards, Option+L or AltGr+7 now type @ and { instead of triggering a shortcut. Clipboard access without a user gesture waits for the permission model.
- ✓Sprint 485M92 CLOSES: POLY-OS runs as a desktop appshipped
The record of the native machine sums up the first step of the arc toward an installable POLY-OS. The operating system written in POLYTONE now runs in its own window on macOS, Windows and Linux. Its frames are pixel for pixel the codec's, its disk is the user's alone, and its programs run in the background. A review found nothing in the drawing and twelve problems in the host, and all but one are fixed. Next comes the server version: the same program serves POLY-OS to every user in a browser.
480–481Phase M91 — The third round
A third fresh held-out round, written by an agent that had seen no rule: ptc fix alone still repairs none, and the cheap blockers it shows are fixed.
- ✓Sprint 480M91 OPENS: a third fresh round gives the same answershipped
A fresh agent that had read only the skill wrote ten more programs on new topics, in the habits of Python, Rust, TypeScript, Go, Java and Swift, with more foreign syntax than the rounds before. ptc fix alone repairs none of them, with the evening's toolchain or today's. Today's applies three times as many fixes, 82 against 27, two of them new in this sprint: the lexer's second tab message now triggers the indentation fix, and a one-line Python docstring becomes a comment. The rest needs a model: Swift's $0 closures, Java constructors, impl blocks, a match used as an expression, multiple returns.
- ✓Sprint 481M91 CLOSES: the night in one tableshipped
The record of the third round sums up the night's eleven phases. Failing foreign habits in the corpus grew from 157 to 224; 223 of them now name a form and 170 get a quick fix, against 68 the evening before. A program written from the card with a few habits left in now repairs itself, 11 of 12. Three fresh rounds of programs in another language's syntax stayed at zero for ptc fix alone, and in an agent's hands such programs pass with the old toolchain and the new alike. The web runtime shrank from 1376 to 1118 KB. The next phases turn to POLY-OS as an installable system.
478–479Phase M90 — The leftovers II
The safe forms the last reviews carried: visibility words, pub struct, a lone brace in a text, Java's boxed type names, Go's parameter order.
- ✓Sprint 478M90 OPENS: public, static, pub struct, a lone brace, Boolean and Go's parameters are fixedshipped
The forms the last reviews carried. public and export become pub. private, protected, internal and static are dropped, because an item without pub is already private to its file. pub struct is taught like struct, and pub struct Name { becomes pub record. A text that holds a single brace, like "{", has it escaped. Boolean, Integer, Double and Long are named as Bool, Int and Float. Go's parameter n int becomes n: int. 168 of the 222 failing priors get a fix. The fresh held-out round of the last phase is still 0 of 10 after ptc fix alone, though more fixes apply there now. That round has now been learned from as well, so the next phase needs a new one.
- ✓Sprint 479M90 CLOSES: modifiers come in runs, and a parameter's type decides the fixshipped
The review checked all 820 tracked POLYTONE files: none hits a new message, and public, static and export stay valid as names. It found that the Go parameter fix also rewrote Java's int n into int: n. It now fires only for a lower-case name followed by a type, and the type-first form stays taught. Modifiers came in runs that stalled one word later: public static fn, export default fn, export pub fn. A run is now consumed whole. It becomes pub when it held public or export and a declaration follows, and it is dropped otherwise, so public let inside a body becomes let. Inside a record, static now teaches like everywhere else.
476–477Phase M89 — The cross-check
The night's quick fixes cross-checked where they are used: two Haiku agents repair ten foreign-syntax programs, one with the evening's toolchain and one with today's; both succeed, the old one in fewer calls.
- ✓Sprint 476M89 OPENS: in an agent's hands the evening's toolchain repairs foreign code as well as today'sshipped
The last phases measured ptc fix alone. This one cross-checks the night's work where it is used: two Claude Haiku agents got the same ten programs in other languages' syntax and the same skills, one with the toolchain of the evening before and one with today's. Both made all ten pass without weakening an assertion. The one with today's toolchain needed more calls, 52 against 43, because it ran ptc fix first on every file and then rewrote each file by hand anyway. One intermediate state misled it: a C-style loop had been fixed to a for line still ending in a brace, which it read as a non-canonical suggestion. That fix now writes the colon head and removes the closing brace in the same edit, or it declines. The baselines of the fix measurements were also checked again with the old stdlib set, and they are identical.
- ✓Sprint 477M89 CLOSES: the skill says what ptc fix is forshipped
The cross-check showed that an agent repairing a file written in another language's syntax rewrites it from the language card anyway, so running ptc fix first only adds calls. The skill used to tell agents to run every hinted fix before editing by hand. It now says that ptc fix is for a few habits left in a POLYTONE file, and that a file in another language's syntax is quicker rewritten from the card in one pass. The phase record also sums up the night: 215 of 216 foreign habits now teach a form, 162 get a quick fix, a lightly foreign program repairs itself, a fresh round of foreign-syntax programs does not, and the web runtime is 258 KB smaller.
473–475Phase M88 — The braces
The forms that stopped every held-out program: brace blocks, semicolons, C-style loops, bare assignments, structs, TypeScript return types; measured on a fresh held-out round at the close.
- ✓Sprint 473M88 OPENS: braces, semicolons and C-style loops are converted for the whole file at onceshipped
The held-out round showed that programs in another language's syntax stop at forms no fix converted. The first ones are now handled for the whole file at once. Every semicolon that ends a line goes in one fix, where before each took a fix of its own. Brace-bodied blocks become colon blocks: fn, if, else if, else, for, while and match, with the closing braces removed. A C-style for (let i = 0; i < n; i++), or Go's for i := 0; i < n; i++, becomes for i in lists.range(0, n) with the import. A bare total = 0 becomes mut total = 0 when the function assigns it again, let otherwise. struct Point { and interface User { become records. 154 of the 208 failing priors get a fix.
- ✓Sprint 474TypeScript return types, Go packages, Kotlin data classes and filtering comprehensions are fixedshipped
The held-out round named the next blockers. A TypeScript return type fn f(u: User): Bool becomes -> Bool, and the colon fix no longer mistakes it for a missing -> Void. Go's package line is dropped, User[] becomes List[User], and a Go parameter xs []int becomes xs: List[int]. Kotlin's data class Student(val name: Text, val score: Int) becomes a record. [x for x in xs if x > 0] becomes xs.filter with the element type taken from the parameter. A let that is mutated later becomes mut. Payload variants with several fields convert in a braced enum. The brace fix now converts the blocks it knows and leaves the others alone, where before one unknown block stopped it for the whole file. On the held-out round one program in ten now repairs itself, none before. On the phase's own corpus it is still 11 of 12.
- ✓Sprint 475M88 CLOSES: a fresh held-out round stays at zero, and ptc fix is a pre-step, not the repair loopshipped
The review wrote ten new programs before reading any rule, in the habits of five languages, and measured both toolchains. ptc fix alone repairs none of them, with the evening's toolchain or today's. On the phase's own sets the number rose to 11 of 12 and 1 of 10, but the round no rule had seen stayed at zero. The first blockers are forms whose fix would need a type or a decision no rule can make, such as an untyped arrow lambda or a generator expression. The reading on record is that a model round carries foreign syntax and ptc fix is the step before it. The review also confirmed nine findings in the new whole-file fixes, all fixed. A C-style loop no longer shadows an outer variable. An assignment in both branches of an if is no longer declared inside one of them. A list that is pushed to becomes mut, nested array types nest, and a map named elsewhere is no longer read as an else.
470–472Phase M87 — The measurement
The night's quick fixes measured together on whole programs: twelve programs with foreign habits, ptc fix alone, 0/12 before and 6/12 now; the classes left are fixed next.
- ✓Sprint 470M87 OPENS: ptc fix alone now repairs 6 of 12 foreign-habit programs, none beforeshipped
Six phases added quick fixes one habit at a time. This one measures them together, on whole programs. Twelve small programs were written the way a model that does not know POLYTONE writes them: four in Python's habits, four in Rust's, the rest TypeScript, Go and JavaScript, each with its own tests. ptc fix runs on each with no model involved, and the result is judged by check and test. The toolchain of the evening before, built from its commit, repairs none of them and applies 3 fixes. Today's repairs 6 of 12 with 61 fixes, and every repaired program passes its tests. A closed output pipe now ends ptc quietly, like any Unix tool, instead of with a panic.
- ✓Sprint 471ptc fix alone now repairs 11 of the 12 foreign-habit programsshipped
The measurement named the classes that stopped ptc fix, and each gets a fix. A let that is assigned later becomes mut. A multi-line enum or record in braces becomes an indented block. A JavaScript template literal becomes a double-quoted text. switch becomes match, and a match that needs a catch-all gets case _: with pass, which is what a switch without default did. forEach as a statement becomes a for loop, and def, func, function and fun before a name become fn. Measured again: 11 of 12 programs check and pass their tests after ptc fix alone, against 6 the sprint before and none the evening before. The one left is a difference in meaning, not a habit. Python's min of a list is a value, while lists.smallest gives an Option, and the empty list needs a decision that no automatic fix should make.
- ✓Sprint 472M87 CLOSES: on programs nobody tuned for, ptc fix still repairs noneshipped
The phase's own corpus had tuned the fixes. Sprint 471 fixed exactly what it showed, and 11 of 12 then passed. So the review agent wrote ten more programs before it saw the corpus or the rules, in Python, Rust, TypeScript, Go and Kotlin habits, with more foreign syntax in them. On those, ptc fix alone repairs none, the evening's toolchain and today's alike, although more than twice as many fixes now apply. Each program hits a form no fix converts: brace-bodied blocks, a C-style for, a bare assignment without let, a comprehension, a struct or interface, a match used as an expression. ptc fix also stops at the first error it cannot fix. These are the next levers, and the record says both numbers. The review also found a fix that silenced the checker: case _: pass was added to a match on a Result, which dropped every error. It now applies only to a plain value, and a Map's forEach is no longer turned into a loop over its keys.
467–469Phase M86 — The carry-ons
Thirty-five more habits from Python, JavaScript, Go, Java, Kotlin, Swift and C# are probed; the weak messages and the two wrong lessons (?? as a ternary, not x as a declared-type change) are taught and fixed.
- ✓Sprint 467M86 OPENS: truthiness asks the value's own question, and Go's and Python's statements are fixedshipped
Thirty-five more habits from Python, JavaScript, Go, Java, Kotlin, Swift and C# were probed. Two taught the wrong thing: x ?? 0 was answered as a ternary, and not x on an Option was told to change the declared type. A condition or a not on something that is not a Bool now names that value's own question, x.is_none() for an Option, not xs.is_empty() for a list, n != 0 for a number, and ptc fix writes it. x ?? 0 becomes x.unwrap_or(0), Go's total := 1 becomes let total = 1, let a, b = 1, 2 a tuple binding, return a, b a tuple, and a block header without its colon gets it. A Float expected where an Int stands names .to_float(). 131 of the 188 failing priors get a fix.
- ✓Sprint 468braced bodies, function generics and the other carried habits are taught and fixedshipped
The forms the last review carried, and a few more. enum Color { Red, Green } and record Point { x: Int, y: Int } say that a body is a colon and an indented block, and ptc fix writes it out one member per line. fn first<T>(…) and Vec<i32> take brackets. Rust's field shorthand P { x, y } becomes P(x: x, y: y). "n = " + 1 becomes one interpolated text. forEach and Swift's guard name the loop and the match to write. The struct-literal hint no longer fires on a declaration's head, where it had turned record P { x: Int } into a constructor call. 137 of the 196 failing priors get a fix.
- ✓Sprint 469M86 CLOSES: each type gets its own truthiness question, and fixes stop at commentsshipped
The review checked all 740 tracked POLYTONE files with the old and the new compiler. Nothing that passed before fails, and the first message changed only where intended. It confirmed twelve findings. The truthiness fix wrote is_empty() on Text, which has none, and 0 for a Float. It now asks each type its own question: t.len() > 0 for a text, f != 0.0 for a float, flag == Some(true) for an optional Bool. Fixes that wrap the rest of a line no longer pull a trailing comment into the parentheses, and a missing colon goes before the comment. Four hints fired on the wrong code and are narrowed: a stray comma in a return, a brace-style if or while header, mut a, b, and := on a name that is already mut, which is now reassigned rather than declared again.
464–466Phase M85 — The Rust habits
Thirty habits from Rust, Go and TypeScript were probed; the fifteen whose message named no form (references, macros, closures, struct literals, angle generics, union and arrow types) are taught, and fixed where one edit is certain.
- ✓Sprint 464M85 OPENS: Rust's references, macros and literals are taught and fixedshipped
Thirty habits from Rust, Go and TypeScript were run against the toolchain. Fifteen got a message that named no form, and println!("hi") was even taught as a postfix unwrap. The Rust half is now answered. &self, &mut self, &T and f(&x) say that there are no references, and ptc fix drops the ampersand. Macros say there are none and name the call or literal; println! with {} placeholders becomes print with an interpolation, vec! a list literal, assert_eq! an assert, format! the text. P { x: 1 } becomes P(x: 1), Vec<Int> takes brackets, and a mut parameter, a |x| closure and a :: path each name the canonical form. A Rust-flavoured program becomes valid POLYTONE through ptc fix alone, in five runs. 119 of the 174 failing priors get a fix.
- ✓Sprint 465TypeScript's types and Rust's iterator chains are taught and fixedshipped
The other half of the habits. A function type written (x: Int) => Int becomes fn(Int) -> Int, an optional field limit?: Int becomes limit: Option[Int], and a comma after an enum variant goes. A union type Int | Text says there are none and shows the enum to declare instead. Rust's xs.iter().map(f).collect() is taught: a List is used directly, and ptc fix drops the .iter() and the .collect(). A TypeScript-flavoured program becomes valid POLYTONE in two ptc fix runs. 123 of the 179 failing priors get a fix.
- ✓Sprint 466M85 CLOSES: every hint looks at its context, and the priors are committedshipped
The review built the old and the new compiler and checked all 542 tracked POLYTONE files with both; nothing that checked before fails now. It then confirmed twelve findings. The worst: assert_eq!(a, b or true) became an assert that always passes, and the operands are now put in parentheses. Two hints fired on valid look-alikes: the union-type hint on an or-pattern, the generics hint on a chained comparison, and the function-type message on a tuple type. Each now checks its position, its spacing or the token after it, and a tuple type is taught as Tuple[Int, Text]. Fixes that would change what a program does are declined: vec![0; n], struct update syntax, f(&mut xs), print!. assert! is a macro too now. The priors of the last two sprints had never been committed, because the ship script ignored a failing git add. They are in this commit, and the script now stops on that failure.
462–463Phase M84 — The calculator
Run 14 asks agents for a tokenizer and a precedence parser with column-exact errors; both models pass, and the three messages they met without a form are taught.
- ✓Sprint 462M84 OPENS: agents write a calculator, and three messages learn to teachshipped
Run 14 is a second large task of a different kind: a tokenizer and a parser with operator precedence, errors that name their column, in three modules. Both models passed every hidden test and all four real runs. They found three messages that named no form. A '?' in a test block now says to open the value with match or to compare it whole. A '///' doc line above a record field or an enum variant, a habit from Rust, used to be answered expected a method; it is now taught, and ptc fix makes it a comment. The language card never named elif, and both models had written else if; it now has a rule for branches. 109 of the 161 failing priors get a fix.
- ✓Sprint 463M84 CLOSES: the calculator judge tells the readings apart, and pub fields teachshipped
The review found that the calculator judge passed a parser where unary minus binds looser than plus and minus, because no test had a leading minus before a binary plus or minus; two tests now do. It also passed a blank line that changed the exit code, and the task said only spaces where every solution skips tabs too. Both are pinned and stated. Both recorded agent solutions still pass the stricter judge. From the same review, pub on a record field, a Rust habit, now says that a field is as public as its record, and ptc fix drops the word. The message for ? outside a function names the value's own patterns, Some and None for an Option.
460–461Phase M83 — The larger program
Run 13 asks agents for a three-module ledger about three times the size of the earlier tasks; both models pass, and the gaps they name in the card are filled.
- ✓Sprint 460M83 OPENS: agents write a three-module ledger, and the card names break, continue, true and keysshipped
The earlier agent runs asked for programs of 40 to 80 lines. Run 13 asks for one about three times that size, in three modules: a record for money in cents, a journal of account operations with eight line-numbered errors, and a main that reads standard input, prints to stdout or stderr and returns the exit code. Both models passed every hidden test and all four real runs. Sonnet got no error at all, in 12 calls. Haiku's one error was True and False; it ran the ptc fix that the check named and was green. Both agents named the same gaps in the language card: break and continue, the Bool literals, and a Map's keys. The card now names all three, plus what reading a missing key does. The workspace's ptc now also keeps the output of each call, so the record shows what each agent saw.
- ✓Sprint 461M83 CLOSES: the ledger judge fails every wrong solution the review wroteshipped
The review wrote wrong versions of the reference solution, one bug each, and judged them. A funds check with <= instead of <, under which an account can never be emptied, passed every hidden test, and so did accepting +5 or treating 0.00 as positive. The task also left the order of the checks open, and both agents had picked a different order from the reference. The task now states the order, the accepted spellings of an amount and its limits. The hidden tests pin all of them, and every mutant fails. Judged again under the stricter tests, both recorded solutions fail only the order, which their task had not stated. The record keeps the verdict for the task they were given and says so.
457–459Phase M82 — The belt
The web runtime stood at 1376 of its 1400 KB budget. The embedded stdlib becomes one compressed stream (−232 KB), the code section is measured, and the budget is lowered to what the runtime needs.
- ✓Sprint 457M82 OPENS: the browser runtime is 232 KB smallershipped
The web runtime stood at 1376 KB of its 1400 KB budget. A third of it was data, and most of that data was the standard library's source, embedded so that imports work without a filesystem. The 28 modules are now packed at build time into one compressed stream and unpacked once, on the first call. The runtime drops from 1376 KB to 1144 KB. The first run after loading takes about 3.5 ms longer (19 ms to 22.5 ms); after that nothing changes. Every module keeps every line, so a stack trace inside the stdlib still reads as it does natively.
- ✓Sprint 458the quick-fix rules get their own size-optimized crate, and the budget drops to 1200 KBshipped
A build of the browser runtime with symbol names showed that one function, the quick-fix rules, took 80 KB, and the rules 117 KB in all. Building the whole toolchain driver for size saved 44 KB but made programs run 10 to 30 percent slower, because the virtual machine's generic run loop is compiled inside the driver. That trade was refused. The rules, which are pure text work and run only after an error, moved into their own crate polytone-fixes, built for size; the driver around them stays built for speed. The runtime is 1118 KB, run time unchanged within noise. Since the M81 close the runtime shrank by 258 KB, and its budget is lowered from 1400 to 1200 KB.
- ✓Sprint 459M82 CLOSES: the module list is pinned to the stdlib directoryshipped
The review rebuilt the browser runtime from scratch and got a byte-identical file, ran the smoke and runtime suites against it, and counted the tests on both sides of the crate split (all 52 still there). It found one gap that predates the phase: nothing tied the browser's list of embedded modules to the stdlib directory, so a module added to the stdlib but not to that list checked natively and failed only in the browser. A test now pins the embedded modules, the checker's list and the directory to each other, and the build refuses a source that contains a NUL byte, the separator in the packed stream. Over the phase, the runtime shrank from 1376 KB to 1118 KB and its budget from 1400 to 1200 KB.
452–456Phase M81 — The handholds
Of the 157 foreign habits that fail, only 68 got a quick fix. The mechanical ones (method names, keywords, operators, membership, print forms) get one, held by a fixable ratchet; ptc fix follows an error into an imported module.
- ✓Sprint 452M81 OPENS: other libraries' method names get a quick fixshipped
Only 68 of the 157 foreign habits in the priors corpus got a quick fix; the rest were left to the model's reading, though many are one mechanical edit. The teaching ratchet now also counts the fixable ones and holds that floor. ptc fix renames another library's method to this type's (xs.append(3) becomes xs.push(3), and likewise includes, indexOf, size, strip, items, has_key, delete, insert on a Set), turns m.insert(k, v) into m[k] = v, n.toString() into "{n}", println and console.log into print, lists.max into lists.largest and lists.sort(xs) into xs.sorted(). 85 priors now get a fix.
- ✓Sprint 453keywords, operators and indentation from other languages get a quick fixshipped
ptc fix now repairs the token habits: const and var become let and mut, this becomes self, x++ becomes x = x + 1, === and !== become == and !=, a block comment becomes line comments (Javadoc's stars included), a single-quoted text gets double quotes, default: becomes case _:, and fn main(): gets -> Void when it returns no value. Tabs and 2- or 3-space indentation become 4 spaces per level across the file. A mixed program from Python, JavaScript and Java habits now checks after three ptc fix runs. 96 of the 157 failing priors get a fix.
- ✓Sprint 454membership, is None, slices, loops and print forms get a quick fixshipped
ptc fix now rewrites the expression habits: 1 in xs becomes xs.contains(1) and not in its negation (only when no operator binds into an operand), x is None becomes x == None, xs[1:] becomes xs.slice(1, xs.len()), print() becomes print(""), print("n", 1) one text, str(n) an interpolation, map(f, xs) becomes xs.map(f), import json as j becomes import json with every j. qualified, and for i, x in enumerate(xs) becomes a tuple pattern over lists.enumerate with the import added. A tuple pattern over a Map now says that a Map iterates its keys and that the pairs are m.entries(), and the fix adds .entries(). 108 of the 158 failing priors get a fix.
- ✓Sprint 455ptc fix follows the first error into an imported moduleshipped
When the first error of a program sits in a module it imports from its own directory, ptc fix (and the MCP fix tool) now repairs it there and names the file in the applied fix; before, it stopped and asked for a second call on that module. A vendored module stays untouched, because the lock owns it. All 112 recorded cold-start answers were judged again with today's toolchain and no model: 104 pass after ptc fix (95 when they were recorded). None of these gains comes from this phase's new rules, since an agent that read the language card does not write those habits; the priors corpus does.
- ✓Sprint 456M81 CLOSES: every fix keeps what the program meansshipped
The review ran every new rule against hard cases and found twelve problems, all dealt with. Reindenting a 4-space file with one 2-space slip doubled every line; it now reindents only a file indented in one unit throughout. Three fixes produced programs that checked but behaved differently: a file's own enumerate was replaced, number literals like 1_000 were copied into text as written, and Python slices became a slice call that stops the program where Python clamps. The first two are fixed, and the slice form is taught again instead of fixed. An import alias inside an interpolation is renamed now, and a parameter with that name blocks the fix. ptc check gives no hint for a vendored module and never writes through a symlink. 107 of the 158 failing priors get a fix, up from 68 when the phase opened.
450–451Phase M80 — The type bodies
What the M79 review carried: 'pub with' and a stray doc line in a type's body were answered "expected a method"; both are taught and fixed.
- ✓Sprint 450M80 OPENS: pub with, and a doc line with nothing after itshipped
Two forms in a type's body were answered "expected a method", which names no form. 'pub with Ord:' now says that a with block takes no 'pub', because a trait implementation is as public as its type, and ptc fix drops the word. A '///' line with nothing after it at the end of a block now gets the doc-line message, and the existing fix turns it into a comment.
- ✓Sprint 451M80 CLOSES: the new messages reach every positionshipped
The review found that after a first with block, neither new message fired: an older check answered first that a method came after a with block, which was false there. The parser now looks past doc lines and pub before deciding, so a real method after a with block still gets that error and everything else gets the right teaching. The pub-with message names the actual trait instead of always Ord. The fix handles extra spaces, and a stray doc line at the end of a file without a final newline.
448–449Phase M79 — The second repeat
Run 1's three tasks again with the evening's skills, a second data point for the repeat, and what that run finds.
- ✓Sprint 448M79 OPENS: run 1 again — 10 calls became 3shipped
Run 12 repeats run 1's three tasks with the same judge and models and the evening's skills. Both passed all three again. Claude Sonnet needed 3 ptc calls instead of 10 and was green on every first call. Claude Haiku needed 4 instead of 18. Its one misstep was a doc line above a with block, which was answered "expected a method". It now says that a with block takes no doc, and ptc fix turns the line into a comment.
- ✓Sprint 449M79 CLOSES: a fix removes the whole causeshipped
The review found that the new fix for a doc line above a with block changed only the last line of a two-line doc, so the first line still attached and nothing improved. A blank line in between also stopped the fix. It now walks upward over blank lines and plain comments and turns every doc line of the block into a comment at once. The recorded numbers of runs 1 and 12 were re-judged and match.
446–447Phase M78 — The repeat
Run 2's three tasks again with today's skills and toolchain, against the same judge: does the day's work show in the agent loop?
- ✓Sprint 446M78 OPENS: run 2 again — 15 calls became 4shipped
Run 11 repeats run 2's three tasks with the same judge and models, using the skills and toolchain as they are this evening. Both models passed all three again. Claude Sonnet needed 4 ptc calls instead of 15, and Claude Haiku 9 instead of 20. Its two remaining missteps are taught better now. ptc test prints the same fix hint as ptc check, because an agent that only ran the tests never saw it. And a postfix '!' used as an unwrap is no longer taught as negation: the message names '?' and match.
- ✓Sprint 447M78 CLOSES: every door gives the same helpshipped
The review found that a '!' after a literal, such as true!, was still taught as negation, and its fix wrote truenot. Every value before a glued '!' now counts as a postfix unwrap, a '!' after a space does not, and the negation fix never touches a '!' glued to a value. The fix hint now reaches agents through every door: ptc check, ptc test, and MCP check, test and run. The recorded numbers of runs 2 and 11 were re-judged and match.
444–445Phase M77 — The leftovers
What the last reviews carried: the map arrow habit {"a" => 1} is taught and fixed, and an invalid serve limit is an error instead of being ignored.
- ✓Sprint 444M77 OPENS: the map arrow is taught, a bad serve limit is an errorshipped
Two things the last reviews carried. A map written the Ruby or PHP way, {"a" => 1}, used to get "expected '}' to close the set literal". It now says that a map entry is 'key: value', and ptc fix writes "a": 1. A POLYTONE_SERVE_LIMIT that is not a whole number used to be ignored, so the server ran until stopped. It is now a runtime error that names the value.
- ✓Sprint 445M77 CLOSES: the leftovers, closed smallshipped
The review found no high or medium defect in the map-arrow teaching or the serve limit. Two small things are fixed: the map fix now writes the canonical spacing, so "a"=>1 becomes "a": 1, and a POLYTONE_SERVE_LIMIT whose bytes are not text is an error too, instead of being treated as unset. An empty limit stays an error on purpose, because a service that never stops is the worse failure.
442–443Phase M76 — The process
An agent on the last untested capability: a tool that runs git through proc.run and counts commits per author. What it finds (no lambda form on the card, the '=>' habit) is taught and fixed.
- ✓Sprint 442M76 OPENS: agents run git, and the card shows a lambdashipped
Agent run 10 covers the last capability no agent had used: a tool that runs git through proc.run and counts commits per author, judged by hidden tests under mock_process and real runs against a fresh repository. Claude Haiku and Claude Sonnet were both green on the first attempt. Both said the card shows no lambda and no way to sort by two keys. The card now shows both. The arrow habit after a typed head, fn(x: Int) -> Int => x * 2, used to get only "expected an expression"; it is now taught, and ptc fix turns the arrow into '='.
- ✓Sprint 443M76 CLOSES: every capability tested by an agentshipped
With run 10, all eight capabilities have been used by an agent and judged by real runs. The review checked the new '=>' teaching in every position it could find. It fires only where a program is already wrong, but a block body opened with '=>' was fixed to '=', which still did not check. Now it teaches ':' and the fix writes it right after the type, and the message for bindings no longer talks about functions. The card says that lists.sort_by takes an Int key. The git judge now has ties that only the right rule orders correctly.
440–441Phase M75 — The file tree
An agent on the filesystem operations and command-line arguments, and the editors' list of types and builtin functions pinned to the checker.
- ✓Sprint 440M75 OPENS: a directory tool, and the editors' list matches the checkershipped
Agent run 9 is a tool that reads a directory and writes an index file into it, judged by hidden tests under mock_fs and four real runs on fresh directories. Claude Haiku and Claude Sonnet were both green on the first attempt with no failing call. The one gap: args() was missing from the stdlib reference, which now lists the builtin functions too. The list behind it offered two types that do not exist and lacked the new capabilities, so it is now pinned to the checker. The pin found a checker bug: Stdin, Process and HttpServer were never reserved, so a program could declare a record named Stdin.
- ✓Sprint 441M75 CLOSES: every list names all eight capabilitiesshipped
The review found that reserving Stdin, Process and HttpServer breaks nothing legitimate, and that several places still named only five capabilities or five mocks: an error message, the spec's prelude, the toolchain skill and a test. All now name eight. The stdlib reference shows the constructors apart from the builtin functions and says which mocks need an import to build their argument. Calling a module's record like a function, as in time.Instant(0), now names its fields instead of claiming the type does not exist. The directory judge no longer crashes on a solution that writes nothing, and it checks the argument count and the sort order.
437–439Phase M74 — The workbench, tested
Agents on the capabilities no agent had used: a command-line filter with standard input, standard error and exit codes, then an HTTP server. What they could not find in the skills is taught there.
- ✓Sprint 437M74 OPENS: the workbench, taught where agents lookshipped
Agent run 7 is a command-line filter judged by real runs: standard input in, totals on standard output, a bad line on standard error and exit code 2. Claude Haiku and Claude Sonnet both passed, but only by searching the spec, because the card and the skill never named Stdin, eprint or the exit code. And the stdlib reference said that what it does not list does not exist, while it listed no builtin methods. The card now names the three workbench capabilities, eprint and the exit code. The skill's capability table has their rows, mocks and a filter example, and the stdlib reference lists every builtin method per type.
- ✓Sprint 438Agents write an HTTP serviceshipped
Agent run 8 is a small HTTP service with four routes (a health check, a decoded query with a 400 without it, a JSON item, a 404), judged by hidden tests and five real requests against ptc run. With the new skills, Claude Haiku and Claude Sonnet both answered all five correctly. Both returned serve's request count from main, so the process exited with code 5. The card and the skill now say that a service's main returns 0 after serve, the skill shows a complete service, and the toolchain skill documents POLYTONE_SERVE_LIMIT.
- ✓Sprint 439M74 CLOSES: the method list matches the checkershipped
The review checked every new teaching claim against real runs and compared the builtin method list with the type checker. The list was hand-kept and lacked exactly what this phase taught (the Fs operations, Stdin, Process, HttpServer, List.extend), and it named a Byte type that does not exist. It is now complete, and a test fails whenever it differs from the checker for any builtin type; editors, the playground and the skills all read it. Both agent judges gained held-out cases, so solutions fitted to the examples no longer pass. The server judge also refuses a port that is already serving.
434–436Phase M73 — The registry
The registry path learns to refresh a locked package, and an agent uses a published package: finds it in the index, vendors it over HTTP and calls it.
- ✓Sprint 434M73 OPENS: the registry refreshes tooshipped
Vendoring a locked package from a registry again now takes a newer listed version, and a dependency is refreshed when a dependent needs more than the lock has. With nothing newer, a locked package stays as it is and the output says so. Both vendor paths share one check that runs before the first copy or fetch, and the same ownership rules: a refresh overwrites only its own modules, removes the ones it no longer lists, and keeps why the package was vendored. The guide's packages chapter explains refreshing, and the benchmark page shows the package run.
- ✓Sprint 435Agents use a published packageshipped
Agent run 6 gives Claude Haiku and Claude Sonnet only the URL of a locally served registry. Both found the poster package in the index, vendored it with one command (its dependency came along), read its API from the vendored file and passed the hidden tests. Neither copied the package's code. Their one misstep was the same: comparing an Option with a bare value, where the message advised changing a declared type. It now says that an Option compares only with an Option and to write Some(…), and ptc fix wraps the value.
- ✓Sprint 436M73 CLOSES: check everything, then writeshipped
The review built its own registries and found that the registry path wrote a package's files before its dependencies were checked: a failure further down left new code under an old lock, and a failed fresh install could never be retried. Vendoring from a registry now resolves and checks the whole tree first and writes only when everything holds. A lock from an older ptc can be refreshed. The Some(…) fix no longer breaks member accesses, the Option message no longer hides an error inside the operand, and the registry judge requires a real ptc vendor call.
432–433Phase M72 — The package
Packages were the one workflow no agent had walked: an agent scaffolds a library, passes the publishing gate, and vendors it into an app, judged by the gate and hidden tests on both sides.
- ✓Sprint 432M72 OPENS: agents make a package and vendor itshipped
A new agent task goes past single files: make a library package, pass the publishing gate, then build an app that vendors the library and imports it. Claude Haiku and Claude Sonnet both passed every verdict (pack, the library's hidden tests, the app's hidden tests, the vendored copy). Both wrote sorted(times), which the message explained but no fix covered, so `sorted(x)` now becomes `x.sorted()` and `ptc check` names the fix. Sonnet vendored too early and was refused when it vendored again, so it copied the file by hand. Vendoring a package the lock already lists now refreshes it.
- ✓Sprint 433M72 CLOSES: a refresh keeps to its own modulesshipped
The review checked the new vendor refresh and found that it could overwrite another package's module. The lock now records which package owns each module. A refresh overwrites only its own modules, removes the ones it no longer lists and never lowers the version, and every check runs before the first copy, so a refusal changes nothing. The method fix takes the method from the message (`count(xs)` → `xs.len()`, no longer `xs.count()`) and leaves parses that return an Option, and discarded calls, to the message. The package judge now proves that the app reads the vendored copy.
429–431Phase M71 — Reaching for the tool
No agent used the free repair in three runs, though the check names it. The agent skill now ties the instruction to the hint line the check prints, and a fourth agentic run on tasks chosen for repairable mistakes measures whether agents reach for the tool.
- ✓Sprint 429M71 OPENS: an agent reaches for ptc fixshipped
In three agentic runs no agent used the free repair. The agent skill had told them to read the JSON check, which carries no hint, and mentioned the repair only in passing. Its loop now says: run the check, and when it prints the hint line, run exactly that command before editing by hand. In a fourth run on tasks chosen for repairable mistakes, Haiku used ptc fix for the first time and was green in one round, while its hand correction of another error in the same run took two. The hidden tests now carry a prefix, after an agent named its own test exactly like ours.
- ✓Sprint 430The MCP check names the fix tooshipped
Agents that use POLYTONE through its MCP server — Claude Code, Codex and the others — call the check tool rather than the command line. That tool now ends with the same hint ptc check prints when the first error has a known fix, after the same trial run, and its description and the agent skill tell the agent to call the fix tool on that file first. The trial that decides whether a hint is honest now lives in one place for both.
- ✓Sprint 431M71 CLOSES: the hint beside the payload, and a trial of its ownshipped
The review kept the new hint from breaking anything: the MCP check answer stays pure JSON and the hint arrives as a second item beside it, a path with spaces is quoted so the printed command runs, and every trial run works in its own directory with only the modules the file imports — vendored ones included — so parallel checks no longer lose hints and a large folder no longer costs a second. The phase's finding stands: an instruction tied to the line the tool prints was the first one agents followed.
426–428Phase M70 — The messages
Agents act on the error message, not on the manual. The remaining messages that only listed options now name the form — argument counts with their signature, foreign type names with the POLYTONE type, a question mark on an Option — and a preflight ratchet keeps the share of teaching messages from ever dropping.
- ✓Sprint 426M70 OPENS: the weak messages name their formshipped
The agent runs showed that agents correct code from the message, not from the manual. A pass over the priors corpus found the messages that still only listed options. An argument-count error now names the signature it has to meet, and for Python's d.get(key, default) and print(a, b) the exact POLYTONE form, with quick fixes for both. An unknown type from another language names the POLYTONE type — bool is Bool, Vec is List[T] — from one map the fixes share. A question mark on an Option inside a Result function shows the match to write.
- ✓Sprint 427The teaching ratchetshipped
Every foreign habit in the priors corpus now gets a diagnostic that offers a quick fix or names the form to write — 140 of 144; the other four state a rule that is its own form, such as four spaces per level. A new preflight gate counts this before every commit and fails when the number drops, so a later change cannot quietly turn a teaching message back into a bare complaint. JavaScript's promise chain gained its message along the way: there are no promises — an async call gives a Task, and run gives its value.
- ✓Sprint 428M70 CLOSES: 144 of 148 foreign habits get a form or a fixshipped
The review made the new messages true everywhere: a call to your own function with the wrong number of arguments now names its signature too, a question mark on an Option in a function that returns nothing suggests a plain return instead of an error, print with no arguments is shown the empty line, and Python's get with None becomes a plain get. The teaching ratchet itself became stricter — a crash fails it, a listing of methods no longer counts as a form — and still finds 144 of 148 foreign habits answered with a form or a fix.
423–425Phase M69 — The agent
Coding agents work the real loop on multi-part programs — the agent skills, then check, fix and test through a logging toolchain — so every stumble shows: how many calls, which errors, whether the free repair was used.
- ✓Sprint 423M69 OPENS: agents work the real loopshipped
A new agentic set lets coding agents work the way they would in practice: read the agent skills, write the program, and run check, fix and test through a toolchain that logs every call, until the task is green. Hidden tests judge the result. On a CSV report with a file capability and an exit code, an HTTP handler and a ranking built on a trait and a bounded generic, Claude Sonnet and Claude Haiku both passed all three, in 10 and 18 toolchain calls.
- ✓Sprint 424What agents do with a named fix — and the results on the pageshipped
A third agentic run tested whether naming the fix in ptc check changes what agents do. It did not: both models passed every task again, Haiku corrected each of its three errors in a single round where it had needed three before, but neither ran ptc fix — agents act on a message that names the exact form, while the quick fixes serve the loops that run without a model. A range written 0..n now has a fix as well. The benchmark page gained a section with every cold-start and agentic result, each number pinned to the recorded files.
- ✓Sprint 425M69 CLOSES: agents read the message, and a hint must be trueshipped
Across three agentic runs no agent ever ran ptc fix — each corrected by hand from the message, and a message that named the exact form got every error fixed in one round. The review then found where the new fixes went too far: a JSON escape that turned a real interpolation into text, a module call that named a function that does not exist, a range rewrite inside a slice. All are fixed, and ptc check now names a fix only after a trial run keeps it.
420–422Phase M68 — The cold start II
The cold-start method on POLYTONE's native media documents: fresh models, given only the media skill, write an image, a song, a web page and a 3D model; each document is rendered by the real codecs and inspected — pixels, samples, HTML, triangles. Every first error points at the skill or a decoder's teaching error.
- ✓Sprint 420M68 OPENS: the media skill learns to show its wordsshipped
The cold-start method now covers the native media documents. Given only the media skill, fresh models wrote a simple image, song, page and model perfectly — and failed three of four richer ones: the skill named gradients, drum patterns, tables and cylinders but never showed how to write them, so the models guessed. With the full format specifications they wrote all four. The skill now carries one complete, rendered document per format with its whole everyday vocabulary, a test renders every document in it, and the five wrong guesses get decoder messages that name the canonical form. With the new skill alone, both models wrote all four.
- ✓Sprint 421Programs that write media, and a filmshipped
The second path of the media vertical: a model writes a POLYTONE function that builds an image, a song or a web page as text, and the result is rendered and inspected — a bar chart whose bars stand where the values say, a scale that grows longer when it is slower, a page with every link. Plus a short film with a fade. Given the language card and the new media skill, both cold models solved all four on the first attempt. A palette may again name a colour after a gradient direction.
- ✓Sprint 422M68 CLOSES: modules too, and the reviewshipped
The cold start reached programs of several files: a record, an enum and a trait shared across a module boundary. Sonnet wrote all four on the first attempt, Haiku three; its miss matched an Option with Ok and Err. That error now names the Option's own patterns, a quick fix rewrites the arms, and the card gives the type of to_int. The review of the media work found five small faults — a judge that counted one import twice, a table message that never fired, a gradient hint on a rectangle, a test that could skip a document, a wrong signature line — all fixed.
417–419Phase M67 — The cold start
Fresh models write POLYTONE from the language card alone, on a new set of tasks no model was tuned on; their first attempts are judged against hidden tests, before and after ptc fix. Every first error points at the card, a teaching error or a missing quick fix.
- ✓Sprint 417M67 OPENS: cold models, and two lines of the card they misreadshipped
A new cold-start set of eight tasks measures a model's first attempt from the language card alone, judged against hidden tests before and after ptc fix. Fresh Claude Sonnet solved 8 of 8, Claude Haiku 7 of 8. Both misses traced back to the card: it said a trait's with block holds the trait's signatures, and it never named Text.split, so the models hand-rolled character loops. The card now says the with block holds the methods with their bodies and names .split(sep), and the parser teaches a bare signature inside a with block. A second run with fresh models: 8 of 8 for both, with answers 27 and 37 percent shorter.
- ✓Sprint 418Round two: what cold models get wrong about time, HTTP, regex and JSONshipped
Eight harder cold tasks over the capabilities, async tasks, regex, JSON, generics and sorting halved the first-attempt rate: 4 of 8 for both models. Every first error became a lesson: both models invented time.hour, so the error now names instant.civil().hour and a fix writes it; a Status where an Int was due now says to read .code; a regex quantifier like \d{4} in a text silently interpolated the number 4 and is now a teaching error with a fix; the anchored regex functions gained a search, patterns.find; a stdlib module used without its import is named and imported; misspelt JSON variants and capitalised Bools are repaired. The same first attempts now reach 7 and 8 of 8 after ptc fix, with no model involved.
- ✓Sprint 419M67 CLOSES: the cold start, reviewedshipped
The review compared the new toolchain with the one before the phase and found where the new help hurt: a misspelt local named like a stdlib module was told to import that module instead of being corrected, a capitalised Bool inside an interpolation could no longer be fixed, a field fix landed before a postfix ?, and the new regex search took minutes on a long text. All are fixed and pinned. tasks.all(…).run() is now taught and repaired too, so every cold-start answer set of round two reaches 7 or 8 of 8 after ptc fix.
414–416Phase M66 — The repair manual
Since M62 the toolchain repairs a teaching error that names one canonical form — in ptc, every editor, the playground, MCP, the generation loops and POLY-OS. M66 documents it: a guide chapter whose outputs are re-run against the toolchain, the machine-facing docs, and the free ptc fix told apart from the Pro CLI's model-driven polytone fix.
- ✓Sprint 414M66 OPENS: the guide learns when the toolchain repairsshipped
A new guide chapter shows the repair the toolchain has made since M62: one file with four habits from other languages, ptc fix --check naming the first fix, two ptc fix runs that leave the program checking, and the program running. Every output on the page is re-run against the real toolchain. The chapter says what a fix never decides, and where the same rules run: editors, the playground, MCP agents, the generation loops and the POLY-OS Terminal.
- ✓Sprint 415The Error Lab and the machine-facing docs know the repairsshipped
Seven of the Error Lab's 25 mistakes are repaired by ptc fix by itself, and each of those pages now says so and names the fix. The claim is recomputed from the real rules in a test, so the page can neither promise a fix that does not exist nor hide one that does. llms-full.txt, read by agents, lists what ptc fix repairs and the agent loop check, fix, read what remains, test; it names every MCP tool (fix was missing, now pinned) and the language server's real capabilities. The Pro CLI page separates its model-driven polytone fix from the free ptc fix.
- ✓Sprint 416M66 CLOSES: the repair manual, use-testedshipped
The new guide chapter and the Error Lab were read as a newcomer in both languages and both themes. The chapter now also shows the repaired file, and that file is checked against what ptc fix actually leaves on disk, like every recorded output. The Error Lab's link to the chapter looks like a link. The live site is current again after the GitHub Actions budget was raised, and the full CI now runs on develop at each phase close, while every sprint passes the same gates locally first.
410–413Phase M65 — The other half
Half of the recorded first-attempt failures were filed as needing judgment. Read again, many of their messages name exactly one canonical form: a wrong field name, fields out of order, a missing return, an unqualified enum, + on Text, an empty literal without a type. M65 makes those messages name the form and turns them into quick fixes, measured on the recorded replays.
- ✓Sprint 410M65 OPENS: three recorded failures repair to green without a modelshipped
Half of the recorded first-attempt failures had been filed as needing judgment. Several of their messages name exactly one canonical form. A field name the type does not declare now gets its own message with a did-you-mean, where it used to say the fields were out of order, and every order message names the canonical form. New quick fixes put named fields in declaration order, rename the one wrong field, add a missing return and qualify an imported enum. Of the nine recorded failing replays, ptc fix alone now repairs three, and each then passes its task's hidden tests. A fix on a CRLF line keeps its line ending.
- ✓Sprint 411+ on Text becomes one interpolated textshipped
A model coming from Python or JavaScript joins texts with +. POLYTONE has no + on Text, and the error said so; now the quick fix writes the canonical form: "Hello, " + name + "!" becomes "Hello, {name}!", with calls and indexes interpolated as they are. A chain mixed with other operators, or with text arguments inside a call, is left to the author. The hint for matching an imported enum unqualified no longer puts parentheses after a unit variant. Five new foreign habits joined the priors corpus, and 48 of its forms now carry a fix that removes their error.
- ✓Sprint 412An empty literal takes the type its function returns it asshipped
mut out = [] cannot know what the list will hold, and POLYTONE asks for the annotation. When the function returns that very binding and declares its return type, the quick fix writes that type: mut out: List[Int] = []. The same holds for an empty map, None, Ok(...) and Err(...) bound before they are returned. A binding that is not returned as it is, or a return type of another kind, gets no guess. Two more foreign habits joined the priors corpus; 50 of its forms now carry a fix.
- ✓Sprint 413M65 CLOSES: 20 of 24 recorded first errors in a repairable classshipped
Of the 24 first attempts recorded in the generation baselines, 20 now fail in a class the toolchain repairs without a model (12 before this phase); the other four are decisions the message lists. The close found and fixed what a fix must never do: add a return in the middle of a loop, take a type from the wrong function, undo a correct fix because the same mistake sits twice on one line, or rename a word inside a text. A fix that turns an error into a syntax error is now undone in every repair loop.
406–409Phase M64 — The workshop in the OS
POLY-OS runs, tests and checks POLYTONE programs through its machine, but the self-repair the editors, the loops and the agents gained had not reached it. M64 lets both machine hosts apply the toolchain's quick fixes, gives the Terminal a fix command that writes the repaired file back, and drives it all headless as a first-time user. M46 stays owner-gated.
- ✓Sprint 406M64 OPENS: the machine can fixshipped
M64 brings the self-repair into POLY-OS. The machine that runs the operating system — headless in ptc machine and in the browser — now answers a fix request: it applies the toolchain's quick fixes to a program on the OS disk, up to three, and hands back what it fixed and the repaired text for the OS to write. Both hosts answer in exactly the same words, pinned by one disk image and the same expected replies on each side, and the machine protocol documents the new answer.
- ✓Sprint 407The Terminal's fix commandshipped
The POLY-OS Terminal learns fix. `fix hello.pt` asks the machine for the toolchain's quick fixes; when it fixed something, the Terminal prints what was applied and whether the program now checks, writes the repaired file back to the disk and says so. When there is nothing to fix, or no known fix, it only reports. Help and Tab completion know the new command.
- ✓Sprint 408The use test: fix in POLY-OS, and every label in the system fontshipped
POLY-OS was driven headless as a first-time user with a broken program on its disk: check reports the error, fix applies two quick fixes and writes the file back, a second fix finds nothing left, and the program runs. The session found that the OS drew boxes wherever a text carried a typographic dash, quote or ellipsis — every teaching error the Terminal printed since check existed, and some of the OS's own messages. Labels now fold that punctuation to the font's ASCII. The session is pinned as an integration test.
- ✓Sprint 409M64 CLOSES: one program at a time, and a fold that keeps the gridshipped
The review found that two fix requests in quick succession could write one file's repair into another, and that a file deleted or edited meanwhile could be overwritten. The Terminal now runs one program at a time and writes a fix only over the text it was made from. The label fold now maps one character to one, so the editor's columns and cursor stay aligned, and the headless machine strips exactly one .pt from a name, like the browser.
402–405Phase M63 — The yardstick
The generation loops now repair known errors without a paid round, but the benchmark judged only the raw first attempt. M63 puts the same fixes on the command line as ptc fix, gives the benchmark runners a separate pass@1+fix column for the POLYTONE arm — the toolchain's own advantage, labelled as such — and replays the recorded first attempts through it. M46 stays owner-gated.
- ✓Sprint 402M63 OPENS: ptc fixshipped
M63 opens by putting the self-repair on the command line. ptc fix applies the edit a teaching error names, in place and up to three times in a row, prints what it fixed and what is left, and exits successfully only when the file checks afterwards; with --check it just describes the fix. It produces the very same report as the MCP tool, because both now come from one function in the compiler. The phase goes on to make the benchmark see what these fixes do — as a separate, labelled number.
- ✓Sprint 403pass@1+fix — what the toolchain repairs on its own, counted apartshipped
The benchmark runner can now show what self-repair is worth. When a model's first POLYTONE attempt fails, it runs through ptc fix — the toolchain's own fixes, no tokens, no second call to the model — and is judged again. The result is its own column, pass@1+fix, beside pass@1 and the repair round's pass@2e; pass@1 still judges the raw first attempt, and Python and TypeScript show a dash, because their toolchains have no such fixes. The benchmark page explains the column before any run fills it.
- ✓Sprint 404The recorded replays through ptc fix — an honest zeroshipped
Every recorded first attempt kept in the replay set went through ptc fix: none of the nine changed. That is the right answer for this set — these replays were kept because their real fix needs a decision, like the order of a pattern's fields or where a return belongs, and a quick fix must never guess at those. The measurement is now a test: the rules leave every one of these programs untouched. How much the fixable classes are worth will be measured where candidates are saved, in Baseline 12.
- ✓Sprint 405M63 closes — Die Messlatteshipped
M63 closes. The self-repair has a command, ptc fix, that prints exactly what the MCP tool reports; the benchmark has a separate pass@1+fix column for what the toolchain repairs on its own, shown beside pass@1 and never inside it; and the recorded replays prove that the quick fixes stay out of mistakes that need judgment. What the fixes are worth in pass rates will be measured, not estimated, in Baseline 12. The record is M63-REVIEW.md.
397–401Phase M62 — Self-repair
The editors offer one-click fixes for teaching errors, but the generation loop still asks a model — and pays for it — to repair what a known edit would. Half of the recorded failed first attempts fall into five fixable classes. M62 adds those rules, lets the Pro CLI and the IDE apply a fix locally before any paid repair round, and hands the fixes to every agent through the MCP server. M46 stays owner-gated.
- ✓Sprint 397M62 OPENS: half the failed first attempts have a known fixshipped
M62 starts from the recorded benchmark runs: 24 first attempts by a model that failed, each with the compiler's verbatim first error. None of them had a quick fix yet — but twelve, half of them, fall into five classes whose teaching error names exactly one edit: asking a map whether it contains a key, a public function without its doc line, a text helper called as a module function instead of a method, a list's length written as a field, and a regex escape that needs a doubled backslash. Every one of these cost a paid repair round with the model; the phase makes them free.
- ✓Sprint 398Five more quick fixes, from the recorded failuresshipped
The five classes are now quick fixes in every editor: .contains on a map becomes .has, a public function without a doc line gets one made from its name, texts.trim(x) becomes x.trim(), .length becomes .len(), and a regex escape gets its doubled backslash. Each rule checks the code before it edits, and the corpus of habits from other languages proves it: 42 of its forms now get a fix that makes their error disappear, up from 30.
- ✓Sprint 399Local repair — the loop fixes what it knows before paying a modelshipped
When a model's first attempt fails with a teaching error that has a known fix, the Pro CLI and the IDE now apply the fix themselves, check again, and only then decide whether the model needs another round. A first attempt that asked a map whether it contains a key is verified after a single model call instead of two, and the CLI reports the repair as fixed locally. At most three fixes are applied per candidate, every one still goes through the full verification, and the two loops do it the same way — the same scenario is tested on both sides.
- ✓Sprint 400fix — the quick fixes for every agentshipped
The MCP server gains an eighth tool, fix. An agent that works on a POLYTONE file — Claude Code, Codex, Cursor or any other MCP client — can now ask the toolchain to apply the edit a teaching error names: the file is changed in place and checked again, up to three times, and the agent gets back what was fixed and what error, if any, remains for it to solve. With check set, nothing is written and the fix is only described. It is the same set of rules as the editors and the generation loops, so every surface repairs the same mistakes the same way.
- ✓Sprint 401M62 closes — Die Selbstheilung, reviewedshipped
M62 closes with a review that ran its probes through the compiler, the MCP server and both generation loops. Two findings mattered most, because the fixes now run automatically: a text containing an escaped quote could be split in the wrong place, producing a program that still checked but printed something else, and a regex escape could be doubled at the wrong backslash, getting worse with every round. Both are fixed; a fix that leaves the same error behind is now undone; character escapes like \u are never touched; the repair prompt shows the model the fixed code its line numbers refer to; and the MCP tool names the file an error is in. The record is M62-REVIEW.md.
394–396Phase M61 — The fast lane
Compiled WebAssembly runs within 1.0–1.7× of CPython since M55; what remains is the traffic between compiled code and the value runtime. M61 counts those crossings per workload first, then fuses the pairs the count shows — execute an instruction and hand the native result straight back — and re-measures. M46 stays owner-gated.
- ✓Sprint 394M61 OPENS: count the crossings firstshipped
M61 opens on the speed of compiled WebAssembly with a measurement before any change. A new counter records how often compiled code calls into the value runtime, per workload: the recursive fib now runs almost entirely natively (11 calls), while sieve makes 1.5 million calls — and every read of a list element costs two of them, one to execute the read and one to hand the result back. synth shows the same pattern for floating-point results, words does not. That is a clear target for the next sprint: one call that executes and returns the native result.
- ✓Sprint 395Fused crossings — fewer calls, the same speedshipped
Compiled WebAssembly now executes a runtime instruction and receives its native result in one call instead of two, and a value that the next instruction branches on comes back natively too. The calls into the runtime dropped by a fifth in sieve and by fifteen percent in synth, and every compiled program still produces exactly what the reference interpreter produces. The honest measurement: the running time did not change. A call into the runtime turned out to be cheap; the time is spent in the work each instruction does there. That is the lever for later, decided by measurement.
- ✓Sprint 396M61 closes — Der Schnellweg, and what the clock saidshipped
M61 closes with its honest result. The calls between compiled WebAssembly and the runtime were counted, the pairs the count showed were fused — a fifth fewer calls in one benchmark, fifteen percent in another — and the programs still behave exactly like the reference interpreter. But the stopwatch did not move: a call turned out to be cheap, and the time is spent in the work each instruction does inside the runtime. The fusion stays, because it is correct and is the shape any faster path will use; the next real speed-up needs a faster representation of lists, decided by measuring where that time goes. The record is M61-REVIEW.md.
390–393Phase M60 — The expressions
Every editor now shares one set of rules, typed by the checker — but only a receiver that is a plain name had a type. M60 has the checker record the type of every receiver it resolves, so a call result, an indexed element or a chain completes, navigates and renames exactly too. M46 stays owner-gated.
- ✓Sprint 390M60 OPENS: every receiver has a typeshipped
M60 opens on the last gap in the typed editors: only a receiver that was a plain name had a type. The type checker now also records, for every member it resolves, the type of what stands before the dot — at the member's own position, so make().grow(), xs[0].len() and p.pos.x each say what they are called on. The record travels the same way as the binding types: off unless an editor asks, on past errors, through the language server and both JavaScript bindings of the browser runtime. Next: the editors use it.
- ✓Sprint 391Every editor follows the receiver typesshipped
The language server, the IDE and the playground now use the checker's receiver types. After make(). or xs[0]. or p.pos. the completion lists exactly the members of what stands before the dot, go-to-definition lands on that type's member, and references count only the uses on that type. To type a receiver while its member is still being typed, the editor asks the checker about a copy of the file with a marker in place of the half-typed name: the checker reports the receiver's type before it complains about the unknown member.
- ✓Sprint 392Method rename through chainsshipped
Because every receiver now has a type, renaming a method also covers calls on a call result or a chain — make().grow() is renamed with q.grow() — where it used to refuse the whole rename. A rename is refused only for a use the checker could not see, such as one in a project file that does not parse, and the refusal names that file's line.
- ✓Sprint 393M60 closes — Die Ausdrücke, reviewedshipped
M60 closes with a review by execution. Its important finding: while a file did not parse, the editor still used the positions of the last successful check, so after inserting a line a rename could change a call on a different type. Those position-based answers are now used only when they are fresh. Going to the definition of an imported type's method no longer lands on a same-named local type, and completion with the cursor in the middle of a word is typed too. Every finding has a test. The compiled-WebAssembly List speed-up first planned for this phase gets a phase of its own. The record is M60-REVIEW.md.
384–389Phase M59 — The playground
The playground is the first thing a visitor touches, and the one editor left without what the language server and the IDE gained: it marked an error only after Run, completed nothing and explained nothing. M59 moves the code intelligence into the shared browser package, then gives the playground live diagnostics, typed completion, hover and one-click fixes, and ends with a use test as a first-time visitor. M46 stays owner-gated.
- ✓Sprint 384M59 OPENS: one code intelligence for every browser editorshipped
M59 opens on the playground, the first thing a visitor touches and the one editor left without completion, hover or live errors. The first step moves the IDE's code intelligence — completion, hover, the typed rules from M58 and the language surface — out of the IDE into the shared browser package that the website and the IDE both use. Nothing behaves differently yet; every test of the IDE, the shared package and the language server's surface pin passes unchanged, and the playground can now reach the same implementation instead of growing a second one.
- ✓Sprint 385Live diagnostics in the playgroundshipped
The playground now checks while you type. A moment after typing stops, it type-checks the whole project in the browser — without running it — and when the visible file has an error, that line is marked and the teaching message appears right below the editor, for example "line 5: unknown name 'totl' — did you mean 'total'?". When the code checks again the mark goes away. The message area is announced politely to screen readers, the mark never scrolls the view or switches tabs while you type, and Run keeps working exactly as before.
- ✓Sprint 386Completion and hover in the playgroundshipped
The playground now completes and explains. Typing a name or a dot opens a list of candidates — the file's own functions and types, imported modules, the language's keywords and built-ins, and after a typed value's dot exactly its fields and methods — and Ctrl+Space opens it on demand; arrow keys move, Enter or Tab accepts, Escape closes. Resting the mouse on a name shows its signature and documentation, or a local's type. The popup and the hover card are the IDE's own, moved into the shared package, so the IDE now runs the very same code; it stays inside the editor's width on a phone and follows both themes.
- ✓Sprint 387Quick fixes in the browsershipped
When the playground's live check finds an error whose teaching message names exactly one fix — else if instead of elif, a misspelt name, x += 1, len(xs), a library function's other-language spelling — a button beside the message applies it in one click, and the check runs again. The rules are the language server's own: they moved into the shared compiler driver, so the editors and the browser run one implementation, and each rule still checks the code at the error before it changes anything.
- ✓Sprint 388The use test — the playground driven in a real browsershipped
The new playground was used the way a first-time visitor would, in a real browser: a misspelt name was marked within a second with "did you mean 'name'?" and fixed with one click; after a list's dot the completion offered only list methods, also inside a text interpolation; resting on a local showed its type. Three findings were fixed on the spot. The completion list was clipped at the bottom of the editor and now opens above the line when there is no room below. In the light theme the editor's code colours were barely readable on white, so the editor is now a dark code surface in both themes, like the site's code blocks. And while a new line was still unfinished, completion fell back to every method — it now finds the types from a repaired copy of the file, as the language server does.
- ✓Sprint 389M59 closes — Der Spielplatz, reviewedshipped
M59 closes with a review that ran its own probes against the new playground. It confirmed four defects, all fixed. The type repair for a half-typed line touched every line ending in a field, so one variable could show the wrong type. A fix clicked just after a keystroke could be applied to the changed line at the old columns and delete the wrong character — a fix now only applies to the exact text it was made for, and typing withdraws it. Each visit to the playground left a listener behind, and after moving the caret the open completion list could take the next Enter instead of a newline. Each has its own test; the record is M59-REVIEW.md.
378–383Phase M58 — The types
The language server reads tokens, not types: after p. it offered every method of every type, and member rename was refused. The type checker already knows the type of every binding; M58 hands those types to the editor — hover shows a local's type, completion after a typed value offers exactly its members, navigation and references follow the receiver's type, member rename becomes possible where every use is typed — and gives the IDE the same. M46 stays owner-gated.
- ✓Sprint 378M58 OPENS: the checker tells the editor its typesshipped
M58 opens on the limit M56 left: the language server read tokens, not types. The type checker now records the type of every binding it sees — each let and mut, each parameter, each name a loop or a pattern binds — and keeps doing so past an error, so a half-finished file still has types. The editor gets them from the same check it already runs on every keystroke, at no extra cost, and the first use is hover: point at a local or a parameter and the editor shows its type, such as p: Point or points: List[Point]. Next in M58: completion, navigation and rename that follow those types.
- ✓Sprint 379Typed completion — after a value's dot, exactly its membersshipped
Completion now follows types. After the dot of a local, a parameter or self, the editor offers exactly what that value has: a record's fields and methods, an enum's methods, or the built-in methods of a text, a list or a map — and nothing else, where it used to offer every method of every type. Because a line being typed rarely parses, the types come from a copy of the file with the half-typed member removed. Writing the test turned up a stale teaching error: main's capability list still named three of the eight capabilities. It now names all eight, and a List[Text] parameter on main is told that the command line is args().
- ✓Sprint 380Typed navigation — two types, one method name, no confusionshipped
Navigation now follows types too. When two types have a method of the same name, going to the definition of c.area lands on the area of c's type, the parameter hint shows that type's parameters, and finding all references of one type's area no longer lists the other's. Built-in types get the same precision: t.split( on a text shows the text split, not the first split of any kind. The parameter hint also learned to look inside text interpolations, where a call like "{c.area(true)}" used to be invisible to it.
- ✓Sprint 381Method rename — where every use is typedshipped
Renaming a method is now possible, under one condition that keeps it safe: every place the method is called on must have a receiver whose type is known. Then the declaration and exactly the calls on that type change, and a different type's method of the same name is left alone. Where safety cannot be shown, the rename is refused with the reason and the place: a call on a value whose type is not visible, a field or variant (they also appear in constructors and patterns), a trait method (its implementations must keep the trait's contract), or a new name the type already uses.
- ✓Sprint 382The IDE learns the typesshipped
POLYTONEide gets what the editors got: the browser runtime now answers the same question the language server asks — every binding of the file and the type the checker gave it — and the IDE uses it. Hovering a local shows its type, and after the dot of a typed value the completion list holds exactly that value's fields and methods, for records, enums and the built-in types alike, self included. The answer comes from the same idle-pause check that already finds errors, and the last good answer stays while a line is being typed. One test drives both JavaScript bindings of the runtime and gets identical rows; another drives the IDE's completion with them.
- ✓Sprint 383M58 closes — Die Typen, reviewed by executionshipped
M58 closes with a review that ran its own probes against the language server and the IDE. It found one way a rename could still break code: a name bound in one arm of a match was taken for the same name in the next arm, so renaming a method through it also renamed a call on a different type. Names now have block scope the way the language has it — a loop or match binder ends with its block — and a record in another module with the same name is a different type to a rename. The IDE no longer borrows a variable from a different function, a field name in a constructor is not taken for a variable, and a method can now be renamed starting from its own declaration. Every finding has its own test. The record is M58-REVIEW.md.
371–377Phase M57 — The toolbox
Real command-line tools and site generators need the same parts every time; a model that has to write them from scratch spends tokens and makes mistakes. M57 adds them to the standard library — paths, a command-line argument parser, Markdown to HTML — proves them with a static site generator, and starts with the compiler naming the intended name when a name is misspelt. M46 stays owner-gated.
- ✓Sprint 371M57 OPENS: a typo names its fixshipped
M57, the toolbox, opens with the cheapest repair there is. When a name or a function is misspelt and no habit from another language explains it, the error now names the one name in scope that was most likely meant — unknown name 'quantty' — did you mean 'quantity'? It names a name only when exactly one is within two edits and at least three characters long; when two are equally close it names neither rather than guess. The editor's quick fix applies the suggestion in one click, two new forms in the habits corpus pin the messages, and the Error Lab shows the case. The plan for the rest of M57: paths, a command-line argument parser and Markdown in the standard library, proved by a static site generator.
- ✓Sprint 372stdlib paths — file paths as textshipped
The standard library gains paths: joining path parts with exactly one slash, the last segment, the parent directory, the extension and the name without it, replacing an extension, folding a path to its shortest form, and the relative path from one directory to another. They are plain functions over text — nothing here touches the disk, which stays the Fs capability's job — so they behave the same in tests, in the browser and in compiled WebAssembly. Every rule is pinned by the module's own tests, including the edge cases a hand-written version usually gets wrong: dot files, a trailing slash, the root's parent, and leading '..' on relative paths.
- ✓Sprint 373stdlib cli — declare the command line, parse it, print its helpshipped
The standard library gains cli. A program declares what it accepts in one chained expression — flags, options with a fallback value, required positional arguments — then parses args() against it and gets every value by name. The usual spellings all work: --out dist, --out=dist, -o dist, -odist, combined short flags like -dv, and -- to end the options; -h and --help always succeed. Mistakes come back as errors that name what was expected: an unknown option lists the ones that exist, a missing argument shows the usage line. The help text is generated from the same declaration, so it can never disagree with the parser.
- ✓Sprint 374stdlib markdown — Markdown to safe HTMLshipped
The standard library gains markdown: the everyday subset of Markdown rendered to HTML — headings with anchors, paragraphs, fenced code with its language, block quotes, bullet and numbered lists with nesting, horizontal rules, and inline code, bold, italics, links and images. It is safe by default: every character of text is escaped, raw HTML is shown rather than run, and a javascript: link renders as plain text. It also gives a document's title and the anchor slug for any heading. Four hundred sections render in well under a tenth of a second.
- ✓Sprint 375The proof — a static site generator, and an honest token countshipped
The three new modules work together in one program: examples/site turns a directory of Markdown files into linked HTML pages — cli reads the command line, paths names the output files, markdown renders each page — with drafts left out unless asked for and a clear error and exit code on a bad option. A test runs it for real. Its Python twin, written with argparse, pathlib and the markdown package, needs fewer tokens: 701 against POLYTONE's 902. That is recorded as measured, beside the launch kit where POLYTONE needs half of Python's: where every step is a mature Python library one-liner, Python is cheaper; where the native media documents do the work, POLYTONE is.
- ✓Sprint 376The toolbox in the guide and the specshipped
The new modules are documented where people and models look. The guide gains a chapter on the toolbox: a table-of-contents tool that reads its options with cli, names its output with paths and anchors its headings with markdown, run for real with its output recorded and checked on every preflight. The language specification lists paths, cli and markdown (and process, which the list had missed) with the reasoning behind their design: a command line declared once cannot disagree with its help text, and Markdown output is safe without a separate sanitizer step.
- ✓Sprint 377M57 closes — Die Werkzeugkiste, reviewedshipped
M57 closes with a review of its three new modules, every finding re-run before it was fixed. The important one was a security hole: a javascript: link with a tab or a line break inside it slipped past Markdown's check and became a live link in a browser. The check is now an allow-list — only web, mail and relative links pass, and nothing with whitespace inside. Ten smaller findings are fixed too: changing the extension of a path with a trailing slash or a trailing dot, relative paths that climb above their start, negative numbers as arguments, help inside combined flags, -o=value, and a command-line declaration that uses one name twice. Each has its own test. The record is M57-REVIEW.md.
364–370Phase M56 — The language server
The editor extension shipped in M52 talks to a language server that still answered only diagnostics, hover and formatting. M56 gives it the rest of what an editor asks on every keystroke — outline, go-to-definition, completion, signature help, references, rename and quick fixes from teaching errors — sharing one language surface with the IDE so the two cannot drift. M46 stays owner-gated.
- ✓Sprint 364M56 OPENS: the language server finds its way — outline and go-to-definitionshipped
M56 opens on the gap behind the editor extension: the language server still answered only diagnostics, hover and formatting. It now gives editors an outline — every record, enum, trait, function, constant and test, with fields, variants and methods nested — and go-to-definition: from a local to its binding, from a parameter to the signature, from a name to its declaration, from Type.member to the member, from value.method to the method, and from module.name into the imported file, resolved exactly the way the loader resolves imports. Both read the lexer's tokens instead of the parse tree, so they keep working while a body is half-typed.
- ✓Sprint 365Completion in every editor — the IDE's rules, one shared surfaceshipped
The language server now completes. It follows the same rules as the POLYTONE IDE: after a module name exactly that module's public functions and types, after an enum name its variants, after any other dot the methods of the types in scope and then the built-in ones, each labelled with the type it belongs to; bare, the local names bound above the cursor, the file's own declarations, the imported module names and the language's keywords, types and functions — with each declaration's documentation beside it. The keywords and built-in methods come from one file generated from the IDE's own list, and the IDE's tests fail if it goes stale, so completion in VS Code and in the IDE cannot drift apart.
- ✓Sprint 366Signature help — the parameter you are typingshipped
While a call is being written, editors now show the callee's parameters and highlight the one the cursor is on — for the file's own functions, record constructors with their named fields, functions of imported modules, enum variants with payloads, methods declared in the project and the built-in methods and functions. The call is found by reading the text before the cursor, so it works on the half-typed line that is the normal state while typing, also when the call spans several lines. This sprint also fixes a lint finding from the previous sprint that had turned the continuous integration red, and the shipping script now runs the linter and the IDE tests before every commit.
- ✓Sprint 367Find references and rename — across the project, refusing what it cannot verifyshipped
Editors can now find every use of a name and rename it. A local or parameter is found inside the function that binds it, including inside text interpolations; a top-level function, type or constant in its own module and as module.name in every file next to it that imports the module. Rename turns that into one edit across all affected files, and refuses with a teaching message when the new name is a keyword or a builtin, is not a valid name, or is already taken — a rename never merges two names. Fields, variants and methods can be found by name but not renamed: without knowing each receiver's type, a rename could change an unrelated type, so the server says how to do it safely instead.
- ✓Sprint 368Quick fixes — a teaching error becomes its editshipped
POLYTONE's errors name the canonical form to write instead; now the editor can write it. When an error names exactly one fix — else if becomes elif, let mut becomes mut, True becomes true, null becomes None, a trailing semicolon goes, && becomes and, an f-string prefix goes, int becomes Int, x += 1 is written out, len(xs) becomes xs.len(), a misspelt library function or method gets its real spelling, and an unknown name becomes the one nearest name in scope — the lightbulb offers it. Every rule checks the code at the error before it changes anything. The corpus of habits models bring from other languages pins the result: for 30 of them a fix is offered, and each one, applied, makes its error disappear.
- ✓Sprint 369The editors catch up — every capability, end to endshipped
The editor side of M56. The VS Code extension's test now drives every new capability through the same transport the extension uses, against the real language server: the outline, go-to-definition, completion, signature help, references, rename and a quick fix. The setup notes for Neovim and Helix name the keys that now work — Neovim's built-in mappings for definition, references, rename and code actions, Helix's goto, picker, rename and code-action keys — and the guide gains an Editors chapter on installing the extension, connecting any LSP client, and what the server does and deliberately refuses.
- ✓Sprint 370M56 closes — Der Sprachserver, reviewedshipped
M56 closes with an independent review of everything the language server gained. It found no crash, but three ways a rename could have damaged code: a parameter used in an if or for header was left behind, renaming a variable in one method also renamed a different variable of the same name in its neighbour, and renaming a function also renamed a method that happened to share its name. All three are fixed, as are seven smaller findings — stricter name-collision checks, a file opened under a differently spelled address now counts as the same file, quick fixes that refuse lines they cannot rewrite safely — and each finding has its own test. The record is M56-REVIEW.md.
354–363Phase M55 — The engine
Opened after the workbench, led by its carried items: the guide catches up with four phases of surface, the web blob gets room back, and the measured GO for typed scalar lowering — compiled WebAssembly running native arithmetic for the 71–89 % of instructions that touch only scalars — is built. Then the media vertical and the benchmark methodology page. M46 stays owner-gated.
- ✓Sprint 354M55 OPENS: the guide catches upshipped
M55 opens with the documentation four phases of surface had outrun. The guide gains six chapters — the whole filesystem, filters with stdin, stderr and exit codes, running other programs, serving HTTP, embedding, and habits from other languages — each with code whose output is the real toolchain's, re-run on every preflight. The Error Lab gains compound assignment, else if and null. 82 recorded outputs now match the toolchain, and the docs artifact is republished. The plan of record for M55: typed scalar lowering for compiled WebAssembly, a blob size pass, the media vertical and the benchmark methodology page.
- ✓Sprint 355Blob size passshipped
The browser runtime gets 149 KB of room back, measured first. A profile of the blob showed its largest single item is the embedded stdlib source; plain comment lines in it are now blanked like test blocks already were, line numbers kept, documentation comments kept because the context slice carries them (−43 KB). Then the compiler's front end — lexer, parser, checker, formatter, module loader — is optimized for size while the VM stays optimized for speed (−105 KB): checking a program costs about 4 percent more time, running one nothing measurable, and the media pipeline benchmark is unchanged. The blob is at 1186 of 1400 KB.
- ✓Sprint 356Typed scalar lowering I — scalar facts reach the backendshipped
The first step of the measured go: the backend learns which values are plain numbers. From the checked program, every function's scalar parameter types now reach the WebAssembly backend as a side table that never enters the serialized program, and two analyses read them — one finds the functions that never need the value runtime at all, the other plans, per function, which locals can live as native integers, floats and booleans. One decision function serves both the planner and the code generator, so the two cannot disagree. Measured honestly: only fib is free of the runtime as a whole function, which is why the next sprint lowers per local instead.
- ✓Sprint 357Typed scalar lowering II — native scalars in compiled WebAssemblyshipped
Compiled WebAssembly now computes with native numbers. Inside every function, locals proven to be integers, floats or booleans live in native WebAssembly locals, and arithmetic, comparisons and branches on them run as native instructions; only the rest goes through the value runtime, with values handed across the boundary where needed. Integer overflow, division by zero and out-of-range shifts are still exactly the VM's errors at the VM's positions — a checked helper hands the operands back and the runtime raises its own message. Deep recursion still meets the VM's limit of ten thousand frames with the VM's message. The compiled workloads run 30 to 54 percent faster, and every fixture still matches the VM byte for byte.
- ✓Sprint 358Typed scalar lowering III — direct native callsshipped
Functions that never touch the value runtime — integers and booleans in and out, only scalar code inside, calling only their own kind — now get a native WebAssembly variant, and calls between them are plain WebAssembly calls without opening a runtime frame. The recursion depth is still counted exactly like the VM's: a module global carries it, synchronized when leaving runtime-managed code, and the ten-thousand-frame limit raises the VM's own message at the VM's own position. Compiled recursive Fibonacci went from 250 to 100 milliseconds — on par with CPython, about half of it Node's own start-up — and every fixture still matches the VM.
- ✓Sprint 359Typed scalar lowering IV — the recordshipped
The lowering's last round and its record. Converting an integer to a float and a float's absolute value and square root now run natively too — each with a WebAssembly twin that rounds exactly as the VM does, while sine and cosine stay in the runtime because two math libraries may differ in the last bit. A runtime instruction that takes one or two native operands now crosses the boundary once instead of up to four times. The record: compiled WebAssembly went from two to five times slower than CPython to between parity and 1.7 times, start-up included, with every fixture still identical to the VM.
- ✓Sprint 360The media vertical I — a launch kit from one fileshipped
The vertical the owner chose, built end to end: from one brand.toml, a POLYTONE program produces a poster, a jingle, a landing page, a teaser film and a 3D badge in about half a second. Each medium is a small text document — image, audio, web, video, model — built by a plain function with interpolation and rendered by the stdlib, so the program contains no pixel loops, no audio math and no HTML escaping. Its tests check that every document renders and that the brand parses, and a proof test runs the whole kit and checks all five files.
- ✓Sprint 361The media vertical II — what it costs a modelshipped
The vertical's number. The same launch kit written as idiomatic Python — Pillow, numpy and the standard library, producing the same five assets in the same sizes — needs 2064 tokens of code where POLYTONE needs 1097: 1.88 times as many. That is the opposite of the general held-out tasks, where POLYTONE's references ran 1.8 times longer than Python's; the native media documents are where the language's design pays, which is why media is the vertical. The measurement script and its results are in the repository; the pass rates for both versions wait for the benchmark keys.
- ✓Sprint 362The benchmark methodology page — Baseline 12's protocol, stated before its numbersshipped
The benchmark page now states how Baseline 12 will be measured before a single number exists: the sealed set of 20 tasks the language never saw (283 hidden vectors, every one listed), the same tasks judged in POLYTONE, Python and TypeScript by one judge, every rate with its Wilson 95 % interval, the card ablation that separates what the language carries from what the card carries, re-judging and reproduction without a key, and the planning estimate of what a run costs. A test derives the page's task list and price from the files that own them, so the page cannot drift from the frozen set.
- ✓Sprint 363M55 closes — Der Motorshipped
M55 closes. Compiled WebAssembly went from two to five times slower than CPython to between parity and 1.7 times, with every error still the VM's exact error; the web blob gave back 177 KB of room; the guide caught up with four phases of surface; the media vertical measured POLYTONE at 1097 code tokens against Python's 2064 for the same launch kit; and the benchmark page states Baseline 12's protocol before its numbers exist. The record is M55-REVIEW.md. Baseline 12 itself still waits for the benchmark keys.
346–353Phase M54 — The workbench
Real programs: the whole filesystem, stdin, args and exit codes, processes, an HTTP server, measured performance and embedding — each behind a capability with a deterministic mock, so the verified loop stays reproducible. M46 stays owner-gated.
- ✓Sprint 346M54 OPENS: the whole filesystemshipped
M54 opens the workbench for real programs, starting with the filesystem: beside read and write, the Fs capability now lists a directory, answers whether a path exists or is a directory, appends to a file, creates directories with their parents and deletes a file or an empty directory — never recursively. The names are the ones models reach for, and Python's or Node's spellings (os.remove, listdir, makedirs, stat) teach the real one. A mock_fs answers from its map without persisting, the browser sandbox changes its virtual files, and the same program gives the same answers on the VM and in compiled WebAssembly against a real disk. One new instruction carries all six operations.
- ✓Sprint 347Exit codes + stderrshipped
A POLYTONE program can now end like a real command-line tool. main returns Void, an Int that becomes the process exit code, or a Result: Ok exits 0, an Err exits 1 with its text on stderr — so a question mark in main ends a CLI the way Rust's main does. The new eprint builtin writes a line to stderr, off the program's stdout. ptc run, the compiled WebAssembly runtime and the MCP server's run tool report the same code; the browser sandbox shows the exit code and marks the stderr lines. exit(), sys.exit and eprintln teach the new forms.
- ✓Sprint 348stdin — the Stdin capabilityshipped
Standard input becomes the sixth capability: main receives it as a Stdin parameter, the host reads the stream whole on first use and keeps it, and the two methods read_all and lines are pure functions of the run — Windows line endings read like Unix ones. Tests build a deterministic one with mock_stdin. With the exit codes of the last sprint, a POLYTONE program is a real filter: read stdin, print to stdout, report on stderr, return its code — and it behaves the same under ptc run and as compiled WebAssembly. input(), readline and sys.stdin teach the form. Also fixed: the foreign-priors pins had not followed the new exit() hint, which turned CI red for one sprint.
- ✓Sprint 349The Process capabilityshipped
POLYTONE programs can start other programs — the seventh capability. main receives a Process, and proc.run takes the argument vector: the first element is started directly with the rest as its arguments, never through a shell, so nothing is split, globbed or expanded and the classic command-injection class cannot be written at all. Any exit code comes back as an Output with the code, stdout and stderr (the new stdlib module process reads it); only a program that cannot start is an Err. Tests use mock_process, keyed by the joined command line; the browser sandbox refuses. A build script that calls git and ptc runs the same on the VM and as compiled WebAssembly. system, subprocess, exec and spawn teach the form.
- ✓Sprint 350HTTP server I — the HttpServer capabilityshipped
POLYTONE serves HTTP — the eighth capability. main receives an HttpServer, and server.serve(port, handler) answers every request with a plain function from http.Request to http.Response: no framework, and the handler is testable on its own. serve becomes a loop over the next request, the handler call and the reply, so a handler runs the same on every backend. Under ptc run the server binds 127.0.0.1 and answers one request at a time, with timeouts, size limits, 405 for unknown methods and no way to split a response through a header; it stops after POLYTONE_SERVE_LIMIT requests when that is set. mock_server feeds a scripted list of requests for tests. A test drives a real server over TCP.
- ✓Sprint 351HTTP server II — the handler's vocabularyshipped
Handlers get the words they need, in the stdlib's http module: text, html and json responses with their content types, a request's path, its percent-decoded query parameters and its body as text, and route, which matches a path against a pattern like /users/:id and hands back the captured segments. Everything is plain POLYTONE with self-tests. A new example, calc_api, is a small JSON service built from them — a test starts it under ptc run on a free port and asks it two real questions.
- ✓Sprint 352Perf measurement — the first CPython numbersshipped
Four workloads — recursive calls, a sieve, a synthesizer loop, text and maps — each with a CPython twin that prints the same answer, timed under the VM, as compiled WebAssembly and under CPython. The VM is on par with CPython: slower on deep recursion, faster on Float math and on text with maps. Compiled WebAssembly is two to five times slower than CPython, and slower than the VM, because every value operation is a call into the runtime. A new profiler (a separate build of ptc) shows why that is fixable: 71 to 89 percent of all executed instructions touch only scalars. Decision recorded: go for typed scalar lowering in the WebAssembly backend.
- ✓Sprint 353M54 close — embeddingshipped
POLYTONE can live inside other programs. The Host trait is now a documented public contract: six required methods, every capability added since with a default, so an embedder written today keeps compiling. JavaScript gets an importable host: runModule takes a compiled module and returns its stdout, stderr and exit code, with closed defaults — no environment, no processes, empty input unless the embedder grants them; the command-line runner is that same host bound to the process. Worked examples for Rust, Node and Python. M54-REVIEW.md closes the workbench: eight capabilities, all with deterministic mocks, all identical on the VM and in compiled WebAssembly.
340–345Phase M53 — The teaching
Opened right after the doors: the residual classes, taught. Forms from other languages name POLYTONE's form, one stdlib name per concept, stacked errors in match arms surfaced in one round, the open LLM-first decisions measured — every change pinned by the compiler's own corpora; the held-out set is never tuned on, only measured once the owner's keys allow. M46 stays owner-gated.
- ✓Sprint 340M53 OPENS: foreign priors teachshipped
A model writing POLYTONE carries the habits of the languages it saw most. 107 of them — def, var, class, impl, try and throw, switch, else if, x += 1, ===, the conditional expression, f-strings, single quotes, lambdas and arrow functions, await, null, True, this, membership with in, slices, comprehensions, import from and as, startsWith, toUpperCase, unwrap, toString — were written into real programs and run through the compiler: 51 ended in a generic error that named no fix. Now every one of them names POLYTONE's form, and the form another language spells for a method that exists is answered with the real spelling. The hints are consulted only on the error path, so no valid program changes; a corpus of the 107 forms pins every diagnostic verbatim, and the spec gains §8.5.
- ✓Sprint 341The stdlib naming auditshipped
One name per concept, and every other library's name answered. The Run-11 residual is closed at its root: a toml table now walks its key/value pairs with entries(), exactly like a Map, and a record's method error lists its fields too — the message that hid pairs is what sent the repair to an invented method. Calling a stdlib function by another library's name now names the real one: lists.max → lists.largest, lists.length → xs.len(), json.stringify → json.to_text, texts.join → xs.join, maths.sqrt → x.sqrt(), time.now → the Clock capability, uuid.uuid4 → uuid.v4, and a camelCase spelling of an existing function or method → its real spelling. The priors corpus grew to 131 forms, every diagnostic pinned.
- ✓Sprint 342Stacked errors I — the skipped statement's insidesshipped
When a statement fails, the checker has since Sprint 323 skipped every later statement that uses what it would have bound — no cascade, but also nothing from inside those statements. Now a skipped loop, branch or match is still walked inside: its own binders count as poisoned, its case patterns get every check that needs no subject type, and its nested blocks are checked as usual. The recorded json_pluck attempt shows why it matters: the same unqualified-enum mistake three times, and the repair round used to see only the first — now it sees all three at once. Measured against cascades: 273 one-point mutations of valid programs each still yield exactly one error.
- ✓Sprint 343Stacked errors II — typed placeholders and the cascade gateshipped
A failed let with a type annotation still says what the name is, so the name is now bound with that type instead of being poisoned: its later uses keep being checked, and a misuse three lines down surfaces in the same round while correct uses stay silent. More errors per round must never mean wrong errors, so a new gate guards the whole several-errors machinery: every valid program in the repository — examples, benchmark corpus, held-out references, fixtures — is mutated at one point (a misspelled constructor pattern, a let or annotated let bound to an unknown function, a loop over one), and each of the 331 mutants must yield exactly one diagnostic. It runs in five seconds, in preflight and CI.
- ✓Sprint 344LLM-first decisions: async methods, nested functions measuredshipped
Two open language questions, decided by what helps a model. Async methods are in: a record, generic record or enum may declare async fn, and calling it yields a Task exactly like an async top-level function — on the VM, in the compiled WebAssembly and across modules. Models write async methods constantly; the teaching error that forbade them bought nothing. Only an async mut-self method stays refused, with the reason: the receiver is rewritten at the call while a Task runs later. Nested named functions stay out, measured rather than guessed: across all twelve recorded benchmark runs — 407 tasks, 455 attempts — not one attempt failed on a nested function.
- ✓Sprint 345M53 closeshipped
M53-REVIEW.md: the residual classes taught. 51 foreign habits that ended in generic errors now name POLYTONE's form, other libraries' stdlib names answer with the real ones, the toml residual is fixed at its root, stacked errors surface together while 331 one-point mutants prove no false error was added, and async methods arrived while nested functions were measured and left out. The held-out cold delta is carried to Baseline 12: it needs the owner's API keys, and the runner measures it with one command. Four lessons, the first: measure the class before writing the fix.
334–339Phase M52 — The doors
Opened while M51 waits for the owner's API keys: every agent and every editor. The skills for Codex, Gemini CLI, Copilot and Cursor beside Claude Code and Qwen Code, MCP for every client, a VS Code extension with the language server, a tree-sitter grammar and a GitHub Action, and — with the free download — a one-line installer. Everything is built autonomously; publishing waits for the owner's accounts (D4). M46 stays owner-gated.
- ✓Sprint 334Every agentshipped
M52 opens while M51 waits for the owner's API keys. The skills reach every coding agent now, not only Claude Code and Qwen Code: install.sh codex, gemini, copilot or cursor copies the three skills to .polytone/skills/ and writes the agent's own always-loaded file — AGENTS.md, GEMINI.md, .github/copilot-instructions.md or a Cursor rule — with the one POLYTONE paragraph byte-for-byte plus a line naming where the skills are. An existing AGENTS.md keeps its content and gets the section once; a second run changes nothing. The PowerShell twin does the same on Windows, and every SKILL.md carries the open Agent Skills format's license field. The gate runs the real installer against a scratch project for every agent.
- ✓Sprint 335MCP for every clientshipped
polytone-mcp --config prints the paste-ready registration block for Claude Code, Qwen Code, Cursor, VS Code with GitHub Copilot, Codex and Gemini CLI, with the binary's own absolute path filled in and the file it belongs in named beside it; Windows paths are escaped so the JSON and TOML stay valid. One module is the source, and a test pins every block in the MCP documentation against what the binary prints. A small npm package is ready to publish: npx polytone-mcp finds the native server and passes stdio through, or exits with the install instructions instead of hanging the client. Found on the way: CI had been red since Sprint 329 — the held-out judge resolved a relative ptc path from its scratch directory; it resolves it first now.
- ✓Sprint 336VS Code extension I — the grammarsshipped
POLYTONE gets a VS Code extension, first its declarative half: a TextMate grammar for .pt (keywords exactly the lexer's, doc comments, text interpolation with its brace escapes, hex and float literals, types, calls, operators; a block comment is marked illegal because the compiler refuses one), a second grammar for the five media documents (header, keys, drawing commands, colours), and language configurations with 4-space indentation after every colon and doc-comment continuation. The tests run the grammars through the engine VS Code itself uses, against a real program and every shipped example. Writing the keyword gate found the playground highlighter missing with — fixed, and pinned to the lexer's table too.
- ✓Sprint 337VS Code extension II — the language clientshipped
The extension comes alive: a language client starts polytone-lsp for every .pt file — the teaching diagnostics as you type, hover, and Format Document in the canonical form. It finds the server through a setting, PATH, or ~/.polytone/bin, says once when none is installed and keeps highlighting, and restarts on a settings change or by command. A test drives the real language server over exactly the transport the client uses: a broken file yields the teaching error that names mut, formatting answers. The extension packages into a 92 KB .vsix, and every tagged release now builds one beside the toolchain archives — Marketplace publishing waits for the owner's account.
- ✓Sprint 338tree-sitter + the GitHub Actionshipped
Two more doors. A tree-sitter grammar with an indentation scanner, written from the compiler's own parser: every real .pt file in the repository — 196 examples, stdlib modules, POLY-OS files, fixtures and benchmark solutions — parses with zero error nodes, 53 corpus cases and 33 highlight checks pass, and the README shows the setup for Neovim, Helix and Zed (the GitHub Linguist request waits for the owner's account). And a GitHub Action for repositories written in POLYTONE: it builds ptc from its own checkout, turns every ptc check diagnostic into an annotation on the exact line and column of the pull request, runs ptc test where tests are declared and ptc fmt --check over everything; this repository's CI runs it on its own examples.
- ✓Sprint 339M52 close — the installershipped
The last door is built and stays closed until launch: one line installs the free toolchain — curl … install.sh | sh on macOS and Linux, irm … install.ps1 | iex on Windows — verifying the archive against the release checksums and unpacking ptc, the language server, the MCP server and the stdlib into ~/.polytone/bin, where the VS Code extension and npx polytone-mcp look. The download gate gains the owner's free-toolchain switch (the Pro CLI stays locked either way); it opens at go-live after a fresh release. A generator writes the Homebrew formula from the release checksums. The installer is tested end to end against the real gate: locked, open, and a tampered checksum that installs nothing. M52-REVIEW.md records the phase; publishing to the Marketplace, npm, Homebrew and Linguist waits for the owner's accounts.
328–333Phase M51 — The proof
Opened after a five-agent analysis of the whole project: prove first. A held-out set written without the card, Python and TypeScript arms on the same vectors, a runner for every model family, ablation and reproduction, then Baseline 12 — the table a stranger can reproduce. Plan of record .claude/plans/m51-m55-der-standard.md (M52 the doors, M53 the residuals, M54 the workbench). M46 stays owner-gated.
- ✓Sprint 329Python + TypeScript armsshipped
The held-out set now judges three languages on the same vectors. Each arm gets the same ticket plus its natural declarations (TypedDicts or interfaces, ValueError or Error, None or null); every hidden judge embeds the vectors as JSON; one judge module runs POLYTONE through ptc, Python through python3 and TypeScript through tsc --strict and node. All 60 references are green on all 283 vectors, gated in CI. The first measured comparison is honest and unflattering: the POLYTONE references are 1.8× the length of the Python ones, most on text scanning (3.8× on duration_seconds) — the cost of the stdlib gaps recorded in Sprint 328.
- ✓Sprint 330A runner for everyoneshipped
One call path for every benchmark runner: Anthropic, OpenAI, Gemini and any OpenAI-compatible endpoint, each on its own wire shape, counting only the tokens the provider itself reports (thinking included where billed), with a batch retry that survives rate limits. The new held-out runner takes every task in every language arm n times, keeps every reply verbatim with the model, commit, card and seal hashes, and reports each pass rate with its Wilson 95 % interval. Seven tests pin the wire shapes, the retry, the interval and one task end to end through a local server — no key, no network. The plan for Baseline 12's held-out half: 180 attempts at n=3.
- ✓Sprint 331Ablation + reproductionshipped
The held-out runner can now take the card away: full card, its eight syntax-core rules only, or no card at all (one line naming the language, exactly like the Python and TypeScript arms) — the question the claim needs answered is how much of POLYTONE's score the card buys. A run directory is self-contained: --rejudge re-judges every kept transcript with today's judges and says how many verdicts it reproduced, --reproduce runs the same provider, model, card and n again on a second machine. A zero-cost reference provider drives the whole pipeline end to end (60/60 green, rejudge 60/60), and --estimate prices Baseline 12 before any spend: about 346 k input and 126 k output tokens per model at n=3 — an estimate, labelled so.
- 332Sprint 332Baseline 12next up
Corpus 58 + held-out × POLYTONE/Python/TypeScript × three or more model families × n=3 (owner decision D3).
- 333Sprint 333M51 record + the pagenext up
/benchmark/ shows the head-to-head with confidence intervals and the methodology; M51 close.
- ✓Sprint 328The held-out set + the licenseshipped
Twenty tasks written by an agent that never saw the card, the spec or the corpus — issue-style tickets that name no module, language-neutral (spec + vectors + a Python reference green on all 283 vectors), sealed by hash and gated in preflight; POLYTONE references for all 20, CI-gated. Writing them found a silent defect: a regex quantifier like {4} in a text literal interpolates the Int — recorded, fixed after Baseline 12 measures. D1: the language and its free toolchain are Apache-2.0; POLYTONEide and the Pro CLI are commercial.
322–327Phase M50 — The carried items
Owner-directed right after the M49 close: take on every point the whole-project check carried, measurement first — the blob size pass and the docs artifact (322), several errors per function (323), the load-time peephole (324), field-path moves (325), Display per instance, the context twin and corpus 58 (326), close (327). Baseline 12 waits for the owner's go; M46 stays owner-gated.
- ✓Sprint 327M50 closeshipped
M50-REVIEW.md: every carried item taken on — the blob size pass, the docs artifact, several errors per function, the peephole, field-path moves, Display per instance, the context twin, corpus 58; G12 stays recorded without a measured class, Baseline 12 waits for the owner's go. Five lessons, the first: measure the claim before the build, and say what the measurement says.
- ✓Sprint 326Display per instance, the context twin, corpus 58shipped
An unbounded type parameter now spells as its instance does: "{self.value}" in Box[T] is Money.show for a Box[Money] — static, per generated instance — where it used to print the structural form with no diagnostic. The browser's context slice refuses a position past the end like the CLI (one shared check). Six everyday tasks join the benchmark corpus (52 → 58), each needing a form Sprints 319–320 opened; Baseline 12 measures them.
- ✓Sprint 325Field-path movesshipped
r.items.push(v), self.img = images.set_pixel(self.img, …) and r.a.b[i] = v on a local now run in place: the field's value is taken out, mutated uniquely owned and stored back (TakeFieldLocal / StoreFieldLocal, codec v10). Three 40 000-step field loops went from 10.1 s to 0.37 s, a nested index write from 6.6 s to 0.43 s; a snapshot taken before still never sees a write. M48 F2.1/F2.3 closed except the index receiver xs[0].push(v).
- ✓Sprint 324Perf rotation VI: the load-time peepholeshipped
Profiled first: Store→Load, Load→Load and a comparison into JumpIfFalse are each about a tenth of all executed instruction pairs. Three superinstructions replace the first of such a pair and leave the second in place, so no jump target moves; each counts two steps and two units of fuel, proven by an exhaustive fuel sweep. Native: the five-voice synth −15 %, fib + sieve −8 %, output byte-identical. The browser blob: within noise — V8 does not profit, recorded.
- ✓Sprint 323Several errors per functionshipped
A failed statement no longer ends its function's check: the names it would have bound are poisoned, every later statement that mentions one is skipped, and the rest is checked — two unrelated mistakes in one function surface together, a mistake's consequences never do. Measured first on the stored replays: it rescues none of the recorded benchmark failures (each was dependent or already reported), so it ships for the editor and the independent class, the benchmark claim withdrawn.
- ✓Sprint 322Blob size pass + the docs artifact, multi-fileshipped
The browser runtime embedded the stdlib's test blocks — 135 KB of a 1365 KB blob that never run in a browser; build.rs blanks them with every line number kept (1365 → 1233 KB). opt-level z measured and refused: 120 KB smaller, 1.6–1.8× slower. The 3.9 MB single-page docs artifact could no longer be read whole before a publish and had stopped being republished at 0.48.313; it is now a 798-byte page plus three supporting files, republished to the same link.
314–321Phase M49 — The fourth rotation
The loop's phase types over the surface as it stands after the fourth audit, measurement first: Baseline 11 over corpus 52 (314), then the card or hardening work the run's dataset justifies, perf rotation V, the web-tools use test and the audit slot in the order the numbers dictate; open-ended like M34, closed when the rotation's findings are spent. M46 stays owner-gated.
- ✓Sprint 321M49 closeshipped
The whole-project check closes: 43 findings, 43 fixed; 12 expansion candidates, 6 built, 6 carried with their reasons; four new standing gates; five lessons — the first: a claim that docs are recorded drifts silently until a gate executes it.
- ✓Sprint 320Stdlib build-outshipped
The operations every generator reaches for: texts.fixed for fixed decimals, Map.remove and Set.remove, a bounds-safe List.get, Text.index_of — each taught from its usual other spellings. POLY-OS shows the local time; the stdlib-way hints are checked for arity.
- ✓Sprint 319Teaching + specshipped
Every hint names a form that compiles: a parameter mutation teaches a mutable copy, a missing method names the stdlib function or the element, reflex syntax (&&, /* */, #, ;, t.0, 0..3) teaches its form, with leaves the next comma alone. == became an equivalence (NaN equals NaN, one NaN key). The spec says what ships, and two new gates hold the spec's examples and every guide and Error Lab output to the compiler.
- ✓Sprint 318Toolchain robustnessshipped
The register's tool and newcomer findings: a 5 000-deep prefix-operator chain no longer crashes ptc, the MCP server or the language server; surplus files, wrong-typed MCP arguments and non-.pt files are refused by name; the CLI explains itself (per-command help, did-you-mean); the archive ships the OS, the wasm runner and a headless example the docs had promised; a new gate re-runs every recorded guide output.
- ✓Sprint 317The whole-project check — the register and the webshipped
Five parallel passes by execution plus the web-tools use test in Chrome: 43 findings and 12 expansion candidates in M49-REVIEW.md, each with its sprint. Fixed here: the runtime loads from any page depth (production had 404'd it on every prerendered tool URL), a failed load names itself, a left page stops, the POLY-OS dock fits the screen, no dead prerendered links, every page titled.
- ✓Sprint 316Perf rotation V — the VM itselfshipped
Measured first: the audio synth is the ceiling (a 24-second five-voice score 8.4 s, 712 M instructions), the profile the dispatch loop with a 48-byte Value on every push, pop and store. One frame borrow per instruction, the executor inlined, scalar fast paths and a 16-byte Value: native −40 %, stereo −33 %, OS screenshot −38 %; blob studio −37 %, video −32 %, 3D −28 %. Byte-identical, pinned through the blob; two micro-changes refuted by measurement.
- ✓Sprint 315Hardening from the run-11 datasetshipped
A field read on a List of records lists the ELEMENT's fields and the way to one (the Sprint-268 twin; Error Lab list-field), so run 11's stacked TOML class shows in the first round; config_port/pkg_check name toml.parse's shape, model_parts shows its shape line indented. Cold delta over the five run-11 failures, same generator: 3/5 pass@1 against 0/5, repair 5/5, the three cold passes pinned verbatim. Card v18: no candidate from run 11.
- ✓Sprint 314Baseline 11 (opens M49)shipped
pass@1 47/52 · pass@2e 50/52 over corpus 52 (the Sprint-306 tasks 4/4 cold), generated by claude-opus-5 after Fable's credits ran out — the model on the record, 43/48 · 46/48 on the run-10 tasks. Two residuals, one stacked TOML class; five replays pinned; the harness's task lookup fixed.
308–313Phase M48 — The fourth audit
The audit slot the M47 record named: the M41–M47 surface — everything shipped or fixed since the M45 register — under the full eight-round protocol with four parallel adversarial reviewers, every verdict by execution; the register (308), fix-sprints by round (309–312), the close (313). M46 stays owner-gated.
- ✓Sprint 313M48 closeshipped
The disposition: 54 findings, 52 fixed, one in the spec, one recorded; five lessons; phase complete. Next: Baseline 11.
- ✓Sprint 312Fix-sprint R7/R8shipped
The judge: no stdlib shadowing, helper modules pass, three tasks take a document; the binaries: MCP doc guard, scalar lines, one context file, ECMAScript whitespace in the CLI's fence parser, floored counts, LSP lifecycle.
- ✓Sprint 311Fix-sprint R5/R6shipped
The machine's two hosts answer alike (test-failed, one --check function, one fuel budget, protocol twins, mouse order); the web tools exactly as strict as their codecs (palette preview ≡ export, studio clauses and names, grid never trims, decimal spelling, video names).
- ✓Sprint 310Fix-sprint R3/R4shipped
The codec line in closed form over its visible span (far polylines 0.04 s instead of never), clipped gradients, .pta/.ptw/.ptv wording; POLY-OS settings survive a quote, replies reach a locked screen, closing asks in every app.
- ✓Sprint 309Fix-sprint R1/R2shipped
The capability guard through map/filter, == on values holding a function refused, the projected-call move; the image render dispatcher carries one Image (no copy per op, clipped loops), mesh.color_faces linear.
- ✓Sprint 308M48 opens — the registershipped
Four parallel adversarial reviewers over the M41–M47 surface, every verdict by execution; every S1 fixed ahead.