WoofWare.PawPrint, a Deterministic .NET Runtime

Smaug1231 pts1 comments

Patrick Stevens | Announcing WoofWare.PawPrint, a deterministic .NET runtime<br>Lighting

Toggle navigation menu About Me<br>About This Site<br>Film List<br>Games<br>Home<br>Lifehacks<br>Posts<br>Reading List<br>Top Posts

Announcing WoofWare.PawPrint, a deterministic .NET runtime

I just released an early version of WoofWare.PawPrint to NuGet.<br>PawPrint is a deterministic .NET runtime - think CHESS.<br>It runs the BCL of .NET 10.<br>It interprets IL, shimming out only the BCL&rsquo;s JIT intrinsics and native code: there are no shortcuts.<br>Enough is implemented that it can do:<br>Console.Writeline<br>async void Main(string[] args) {...}<br>Task.Run<br>A whole load of reflection<br>Many of the low-level synchronisation primitives like Monitor<br>It uses a variant of Probabilistic Concurrency Testing when scheduling threads, in an attempt to maximise the exploration of &ldquo;interesting&rdquo; thread orderings.<br>How I decided it was ready<br>I&rsquo;ve taken six standard race conditions and tested that we can deterministically identify them.<br>More concretely, inspired by Deadlock Empire, these tests are of the form &ldquo;demonstrate that some interleaving of threads results in some known bad condition happening&rdquo; (like a deadlock or an exception being thrown).<br>Every test I tried, the test harness found the bug immediately, often taking only a couple of trial seeds.<br>What it&rsquo;s not ready for<br>Well, I expect that if you use it, it will blow up almost immediately.<br>The BCL contains very large amounts of native code, and it must be explicitly modelled in PawPrint for it to execute.<br>An upcoming piece of work will be to allow the user to plug in their own implementations so that they aren&rsquo;t blocked on the built-in incompleteness.<br>Overall design<br>PawPrint is ultimately intended to allow time-travel debugging and control over history.<br>To that end, it maintains an extremely rich internal model of the IL machine.<br>Everything is provenance-tracked; every pointer knows what object/field/method/whatever it&rsquo;s pointing to, and every byte array knows whether it&rsquo;s e.g. &ldquo;a projection of object Foo into raw bytes&rdquo; vs &ldquo;just a bunch of bytes the user gave me&rdquo;.<br>All arithmetic results know whether they are &ldquo;a sum of raw integers&rdquo; or &ldquo;a difference of pointers within the same array&rdquo; or whatever.<br>The use of LLMs<br>Original design is my own, and I started writing it by hand.<br>Sonnet 4.6 came out at some point during this process, and I started using it for reference information about .NET.<br>I also used Gemini 2 Pro to perform fuzzy search through the ECMA-335 spec.<br>Then in 2026 I got the same LLM psychosis everyone else has, and used Claude Opus 4.6/7 and GPT-5.5 to &ldquo;complete&rdquo; it.<br>This was a massive accelerator, and I believe it shaved literally years off the project, at the cost of making the code Claude-shaped in the small.<br>This project is particularly LLM-suited because there is a reference implementation (.NET 10 itself) and a spec (ECMA-335).<br>Errors the bots made<br>Sadly it was still necessary for me to maintain architectural direction during this project.<br>There was only one place where I completely abdicated a complex architectural decision to GPT-5.5 because I was too lazy to decide myself, and that was a disaster which I ended up completely rewriting by hand.<br>That decision was about the handling of the fact that native code and some unsafe casts require genuine byte arrays to compute a result, and there&rsquo;s a bunch of Unsafe.As calls in the BCL which make make it hard to avoid laundering provenance-tracked pointers through flat bytes.<br>I religiously track provenance in PawPrint.<br>GPT-5.5 chose to represent arrays as being located in specific locations in memory, by assigning them fake addresses in a certain range.<br>This got more and more unwieldy over time, and arithmetic operations on them became annoying because we lost their provenance as soon as we decided there was a genuine integer representing their location.<br>Eventually I tore that out and replaced those integers with synthetic &ldquo;I am the address of heap object Foo&rdquo; markers; arithmetic on such objects will generally crash PawPrint, but that&rsquo;s fine because the resulting integer values are generally undefined by .NET anyway.<br>(There is specific support for performing arithmetic on pointers known to be within the same array.)

Published: 2026-06-04<br>Last modified: 2026-06-04 07:41<br>654 words

Submit anonymous feedback

Patrick Stevens

&copy; Patrick Stevens 2026

pawprint rsquo ldquo rdquo woofware deterministic

Related Articles