<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[JC Ghiringhelli]]></title><description><![CDATA[I build tools and methodologies for AI-assisted software engineering. In 2026 I published Generative Specification — a programming discipline for the stateless reader. Based in the US, Spanish citizen, born in Uruguay. genspec.dev]]></description><link>https://ambientengineer.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!xkPt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26a933ca-09c0-47c1-9507-e2438b3412bc_144x144.png</url><title>JC Ghiringhelli</title><link>https://ambientengineer.dev</link></image><generator>Substack</generator><lastBuildDate>Mon, 11 May 2026 08:12:01 GMT</lastBuildDate><atom:link href="https://ambientengineer.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Juan Carlos Ghiringhelli]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[ambientengineer@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[ambientengineer@substack.com]]></itunes:email><itunes:name><![CDATA[JC Ghiringhelli]]></itunes:name></itunes:owner><itunes:author><![CDATA[JC Ghiringhelli]]></itunes:author><googleplay:owner><![CDATA[ambientengineer@substack.com]]></googleplay:owner><googleplay:email><![CDATA[ambientengineer@substack.com]]></googleplay:email><googleplay:author><![CDATA[JC Ghiringhelli]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Flea Game]]></title><description><![CDATA[The game was her idea. The correctness was free]]></description><link>https://ambientengineer.dev/p/the-flea-game</link><guid isPermaLink="false">https://ambientengineer.dev/p/the-flea-game</guid><dc:creator><![CDATA[JC Ghiringhelli]]></dc:creator><pubDate>Mon, 27 Apr 2026 22:43:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xkPt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26a933ca-09c0-47c1-9507-e2438b3412bc_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I built a flea game for my daughter. The game was her idea, the implementation was mine. </p><p>The game is this: a dog is sitting on the floor. Fleas are moving through its fur. The player has three tools. Tweezers &#8212; precise, one flea at a time; miss, and the flea scatters, pulling nearby fleas with it. A comb &#8212; sweep a straight line through the fur and catch everything in its path, but only straight, only so many at once. A spray &#8212; freeze the fleas temporarily, create a window to work. Each scenario: more fleas, darker fur, faster movement. The game escalates in difficulty.</p><p>It took a few hours of the weekend. It might never be seen by anyone outside the family.</p><p>Now ask yourself: should this program be correct?</p><p>Not &#8220;good enough.&#8221; Not &#8220;works on my machine.&#8221; Correct. Free of state corruption when a frozen flea&#8217;s timer expires mid-sweep. Free of race conditions when the scatter effect fires while the comb is still resolving. Free of invalid transitions when the pause button is pressed during the frame a flea is caught.</p><p>The answer, for most of the history of software, was: *of course not. That&#8217;s for pacemakers, ATMs and aircraft. This is a flea game.*</p><p>That was a reasonable answer. It was also a choice that shaped everything that followed.</p><p>---</p><p>## The Agreement Nobody Made</p><p>There is a body of work &#8212; built over roughly 2,376 years, from Aristotle&#8217;s logic through to the formal methods community of the late twentieth century &#8212; that tells you exactly how to make programs correct. Not probably correct. Not correct for the test cases you thought of. Provably, structurally, mathematically correct.</p><p>Tony Hoare gave us contracts: preconditions, postconditions, invariants. If you state what must be true before a function runs and what must be true after, the machine can check whether the implementation honors those promises.</p><p>Robin Milner gave us types that prevent whole categories of error from existing. Not catching them &#8212; preventing them. A well-typed program cannot have certain bugs. Not in the way a tested program has fewer bugs. In the way a circle cannot have corners.</p><p>Naoki Honda gave us session types: protocols that guarantee two communicating processes will always be in compatible states. No deadlock. No protocol violation. Not as a property to be tested but as a structural guarantee.</p><p>Jean-Yves Girard gave us linear types: the ability to say that a resource must be used exactly once &#8212; not zero times, not twice, once. A ticket that cannot be duplicated. A payment that cannot be reversed after it clears.</p><p>Dorothy Denning gave us information flow analysis: the ability to prove that sensitive data can only travel along permitted paths. Not by checking every line of code for leaks but by making unauthorized flows structurally impossible to express.</p><p>Every one of these tools was a genuine breakthrough. And none of them ever reached the flea game.</p><p>Because deploying them required the engineer to have read the papers. To understand the type theory. To annotate the code with the right labels in the right places. To maintain those annotations as the code evolved. The annotation burden was not recoverable at human scale. A weekend project could not absorb the cost of a graduate education in formal methods.</p><p>So a global decision was made &#8212; not in any meeting, not by any committee, not consciously &#8212; that formal correctness would be reserved for systems where lives were at stake. Everything else would ship on convention and hope.</p><p>That decision is now over.</p><p>---</p><p>## What Changed</p><p>The AI assistant has read the papers.</p><p>Not metaphorically. These systems trained on the formal methods literature, on every implementation of Hoare logic and linear types and session type theory ever published. They have internalized the patterns well enough to apply them correctly to new situations.</p><p>What they are missing is not the theory. They have the theory.</p><p>What they are missing is *your system*. They do not know what a flea is in your program. They do not know what frozen means in your animation model, what scatter means in your physics engine, what the comb&#8217;s line constraint means for your hit-detection. Every session starts from scratch. The theory is universal; your system is not.</p><p>Generative Specification is the bridge.</p><p>Write a specification &#8212; a precise description of what your system must be. What a flea is. What frozen means. What the game must never allow, and what it must always guarantee. Not code. Not implementation. The territory, named precisely.</p><p>The AI reads that specification at the start of every session. It holds the theory. You hold the domain knowledge. The specification is the handshake between them.</p><p>When you specify that the game session has exactly five valid states &#8212; menu, scenario select, playing, paused, and ended &#8212; and that the transition from playing to ended can only happen through catching all fleas or expiring the timer, the AI does not just write a switch statement. It builds a declarative state machine with typed transitions and no reachable invalid state. In TypeScript, the machine refuses to process an undefined event &#8212; invalid transitions are practically impossible. In Rust, with Honda&#8217;s session types, they would be formally impossible: the compiler refuses to compile the invalid transition. The specification is the same. The formal weight behind it scales with the language.</p><p>When you specify that a frozen flea carries an expiry timestamp and must transition back to active exactly once, the AI builds a component with a countdown the system removes when it expires. In TypeScript, this is a convention: a dedicated system decrements the timer, removes the component, and a test suite verifies it happens exactly once. In Rust, the same specification produces Girard&#8217;s linear type: the compiler enforces that the resource is consumed exactly once, not by convention but by the type system. You named the constraint. The language determines how strictly it is held.</p><p>When you specify that a miss with tweezers triggers a scatter effect that moves nearby fleas to new positions, and that the first miss in the tutorial scenario does not trigger scatter, the AI builds a system that checks those preconditions before acting and produces a bounded, deterministic outcome. In TypeScript, a test verifies the postcondition holds. In Rust, the same specification lets you formally annotate the postcondition &#8212; what Hoare formalized &#8212; and have it verified. You did not need to know these names. You named the territory. The AI built structures that express the same guarantees those theories formalize. The language determines how strictly they hold.</p><p>---</p><p>## What This Means</p><p>The flea game was built in a few hours. Under Generative Specification discipline &#8212; a specification written before a line of code, behavioral contracts for every tool and state transition, a test harness derived from those contracts and running against the live game. The specification took longer to write than the code took to generate, and even that was assisted by the AI.</p><p>That is not a boast about AI speed. It is a claim about where the work went. The specification carries the complexity now. The implementation follows from it.</p><p>There is no minimum project size for correctness anymore. The inventory tool for a small shop. The scheduling script for a weekend sports league. The game an engineer builds for her daughter on a Saturday afternoon. All of them can now have the structural guarantees that were previously reserved for systems where people would die if they failed.</p><p>This is not a claim about AI being magical. It is a claim about where the cost went. The annotation burden &#8212; the thing that made formal methods inaccessible at human scale &#8212; was the cost of *learning the theory and applying it correctly by hand*. That cost has moved. The AI carries the theory. The practitioner carries the domain. The specification connects them.</p><p>The deal that software engineering made &#8212; correctness for important things, convention and hope for everything else &#8212; was made because the alternative was too expensive. The alternative is no longer expensive.</p><p>---</p><p>## The Practitioner Who Doesn&#8217;t Know She Is One</p><p>There is a second thing the flea game story reveals.</p><p>I wasn&#8217;t thinking about formal methods when I was building it. I was thinking about what the game should do. My daughter wasn&#8217;t thinking about formal methods when she described it. She was thinking about what would be fun. What she gave me was a spec: dog, grass, fleas, three tools with distinct constraints, escalating scenarios. The naming of the territory. That is all GS requires.</p><p>That clarity &#8212; the ability to say precisely what a system must be &#8212; is the only skill that the new model requires. Not Hoare. Not Milner. Not Honda or Girard or Denning. The ability to name the territory.</p><p>That is a human skill. It always was. The reason it was not sufficient before was that between naming the territory and building the system, there was a vast mechanical layer that required specialized training to cross. That layer has been crossed. The naming is what matters.</p><p>The formal tradition spent 2,376 years building the machinery that sits on the other side of the specification. All of it waiting for a practitioner who could describe what she wanted clearly enough for the machinery to engage.</p><p>The same principle &#8212; that naming the territory precisely is sufficient for the machinery to engage &#8212; turns out to apply beyond correctness. It applies to maintainability, to reviewability, to the ability to change a system without breaking what it promised. TDD, SOLID, hexagonal architectures. That is a different essay.</p><p>The flea game is enough.</p><p>---</p>]]></content:encoded></item><item><title><![CDATA[The Ambient Engineer]]></title><description><![CDATA[The monitor screen is the wrong interface for this.]]></description><link>https://ambientengineer.dev/p/the-ambient-engineer</link><guid isPermaLink="false">https://ambientengineer.dev/p/the-ambient-engineer</guid><dc:creator><![CDATA[JC Ghiringhelli]]></dc:creator><pubDate>Thu, 19 Mar 2026 04:53:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xkPt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26a933ca-09c0-47c1-9507-e2438b3412bc_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have not written code in months. Nor application, nor testing, nor deployment, nor command line scripts.</p><p>I have fifteen to twenty projects open at any time, six or seven active in any given session, the others paused at a deploy, a test run, a decision I have not made yet. When one pauses, I move to the next. What I do now is specify: I describe what should be built, why, to what standard, in what shape, with what constraints and failure modes. The model builds it, executes the commands, writes the configuration files, calls the APIs.</p><p>The monitor screen is the wrong interface for this.</p><p>Not because screens are bad. The role that remains in AI-assisted development is an orchestrator who holds intent and decides what comes next, and it does not require a workstation. It requires attention, judgment, and the ability to communicate precisely. I spent twenty years learning to speak the machine&#8217;s language: high level languages, type systems, compiler flags, a new language every two years, a new syntax every project, 30,000 line XSLTs I had to read end to end just to understand what transformation was happening. The machine could not meet me where I think. I had to go to where it lived. That constraint is dissolving.</p><p>What replaces it is three physical layers: a thin display in the field of view, a pocket unit for routing and bridging, and compute wherever it lives. Mini PCs, cloud instances, laptops. Incoming signals arrive already summarized to significance, responses go back by voice without switching physical context. The hardware already exists. What does not exist yet is the orchestration layer: the specification surface that determines which signals rise to attention, at what frequency, prioritized by urgency, clustered and ordered by what actually needs a human decision.</p><p>Imagine gesture-minimizable overlays showing messages alongside summaries and recommended actions, and being able to reply on content and tone just by voicing it. Dynamic calls with real-time captions, translations, automatic action items. Diagrams and decisions saved to folders you summon on command. An ordered queue of inputs needed from your many AI assistants who are building your projects and need occasional but precise guidance. Charts, slides, art, music, video, articles, podcasts, each shaped by describing and constraining the outcome across iterations until it matches what you desire.</p><p>This is the same discipline I apply to software, now applied to the surface that matters most: my own attention. It lets me step away from the screen, move through the world, socialize, exercise, think, while the critical arrives immediately and everything else waits in organized flows rather than noise. It enables the dispersed, asynchronous work that is natural when AI is executing your specification and needs occasional direction, without foreclosing the periods of deep focus that now resolve on much faster cycles.</p><p>When execution cost approaches zero, the limiting constraint on what gets built shifts from effort to the ability to specify correctly and the quality of your attention. That is a more democratic bottleneck. Not perfectly democratic, because specifying correctly is a real skill and skills are unevenly distributed, but the barrier becomes epistemic rather than economic. Understanding replaces capital as the gate.</p><p>I published the theory behind this discipline this week: Generative Specification (https://doi.org/10.5281/zenodo.19073543). The ambient interface is where it leads.</p><p>This is the year the interface catches up. Sixty years of screen, keyboard and mouse. It all resolves to gesture and specifying.</p><p>The methodology behind this: Generative Specification (https://doi.org/10.5281/zenodo.19073543). The tool that implements it: forgecraft-mcp (https://github.com/jghiringhelli/forgecraft-mcp). The workshop for teams: forgeworkshop.dev (https://forgeworkshop.dev).</p>]]></content:encoded></item><item><title><![CDATA[I Had 93% Test Coverage. Then I Ran Mutation Testing.]]></title><description><![CDATA[The number you trust to say "safe to refactor" might be measuring the wrong thing.]]></description><link>https://ambientengineer.dev/p/i-had-93-test-coverage-then-i-ran</link><guid isPermaLink="false">https://ambientengineer.dev/p/i-had-93-test-coverage-then-i-ran</guid><dc:creator><![CDATA[JC Ghiringhelli]]></dc:creator><pubDate>Thu, 19 Mar 2026 03:32:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xkPt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26a933ca-09c0-47c1-9507-e2438b3412bc_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Line coverage measures execution: what percentage of your code runs when the tests run. 93.1% means 93.1% of lines execute. That number is correct. It says nothing about whether any test catches a failure.</p><div><hr></div><h2><strong>Execution vs. Detection</strong></h2><p>A test that calls <code>calculateTax(100)</code> and asserts <code>result !== null</code> gives you full line coverage of that function. It will not catch a wrong tax rate, a sign error, or a rounding failure. Change the return value to anything non-null and the test still passes.</p><p>That test covers the line. It does not test the behavior.</p><div><hr></div><h2><strong>What the Experiment Found</strong></h2><p>In the multi-agent adversarial experiment from the Generative Specification white paper (<a href="https://doi.org/10.5281/zenodo.19073543">https://doi.org/10.5281/zenodo.19073543</a>), the treatment project produced a test suite with 93.1% line coverage.</p><p>Then Stryker was applied to the services layer: 116 mutants, each a realistic code change (flipped boolean, swapped operator, removed return value). Stryker checks whether any test fails when the code is mutated.</p><p>Baseline mutation score: <strong>58.62% MSI</strong>.</p><p>A 34-point gap. That is the fraction of the codebase where a bug can be introduced, your tests will pass, and CI will go green.</p><p>Three rounds of targeted assertion improvements followed, guided by the surviving mutants. Each round replaced presence checks with correctness checks and added boundary conditions.</p><p>After three rounds: <strong>93.10% MSI</strong>, matching line coverage exactly.</p><p>When both numbers converge, every covered line is verified. The tests no longer just execute the code; they detect when it breaks. The line coverage number turned out to be exactly the quality level the tests needed to reach.</p><p>The same pattern appeared in the Shattered Stars case study in the paper: line coverage 80%, mutation score 58%, a 22-point gap. High line coverage alongside a low mutation score is the signature of tests written to satisfy a metric.</p><div><hr></div><h2><strong>Why This Happens</strong></h2><p>This is not unique to AI-generated tests. Human-written suites built after the implementation show the same pattern. Tests optimized for a coverage gate optimize for the gate.</p><p>The structural fix is TDD: write the test first, make it fail, write the code that makes it pass. A test written before the implementation cannot pass until the implementation is correct. The model can follow TDD instructions. It does not do so spontaneously.</p><div><hr></div><h2><strong>The Action</strong></h2><p>Find the mutation tool for your stack. <strong>Stryker is JavaScript and TypeScript only.</strong> Pick the right one for your language.</p><p>   - JS / TypeScript &#8212; Stryker (https://stryker-mutator.io/) &#8212; npx stryker run</p><p>   - Python &#8212; mutmut (https://mutmut.readthedocs.io/) &#8212; mutmut run</p><p>   - Java / Kotlin &#8212; PIT (https://pitest.org/) &#8212; mvn pitest:mutationCoverage</p><p>   - C# / .NET &#8212; Stryker.NET (https://stryker-mutator.io/docs/stryker-net/introduction/) dotnet stryker</p><p>   - Go &#8212; go-mutesting (https://github.com/zimmski/go-mutesting) &#8212; go-mutesting ./...</p><p>   - Ruby &#8212; mutant (https://github.com/mbj/mutant) &#8212; mutant run</p><p>   - Rust &#8212; cargo-mutants (https://mutants.rs/) &#8212; cargo mutants</p><p>Run it on your services layer. Look at the gap between your line coverage and your mutation score.</p><p><strong>If both numbers are close, be proud.</strong> Most codebases do not start there. Every covered line is breaking a test when it changes. That is what a test suite is for.</p><p>If there is a gap, give your AI assistant the list of surviving mutants and ask it to strengthen the assertions. The same model that wrote weak tests can write better ones; it needs the mutation report to know what &#8220;better&#8221; means.</p><p>The full experiment data &#8212; all eight conditions, mutation scores, raw results &#8212; is in the Generative Specification paper (https://doi.org/10.5281/zenodo.19073543). The replication is fully reproducible: Docker, an Anthropic key, and 20 minutes.</p><p>genspec.dev (https://genspec.dev)</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ambientengineer.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Why Your AI Coding Assistant Produces Incoherent Architecture (And What to Do About It)]]></title><description><![CDATA[It's not the model. It's the absence of a discipline that constrains what a stateless reader can derive.]]></description><link>https://ambientengineer.dev/p/why-your-ai-coding-assistant-produces</link><guid isPermaLink="false">https://ambientengineer.dev/p/why-your-ai-coding-assistant-produces</guid><dc:creator><![CDATA[JC Ghiringhelli]]></dc:creator><pubDate>Wed, 18 Mar 2026 13:01:26 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xkPt!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26a933ca-09c0-47c1-9507-e2438b3412bc_144x144.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I&#8217;ve been building software for almost twenty years. For most of that time, the job was understanding the problem, then writing the code that solves it.</p><p>Sometime in the last two years, the second half of that sentence disappeared. I haven&#8217;t written a single line of code for over 9 months.</p><p>I published a paper this week that explains why &#8212; not as a productivity story, but as a formal one. The argument runs through Chomsky&#8217;s grammar hierarchy, Martin&#8217;s sequence of programming paradigm shifts, and six production systems I built under the methodology I&#8217;m proposing.</p><p>The core claim: we are at the first programming discipline of the pragmatic dimension. Every prior discipline constrained what was permitted (syntax) or what communicated to a reader with context (semantics). Generative Specification is the first one that constrains what a stateless reader, a model with no prior context, can derive. The discipline is not about AI features. It&#8217;s about what you have to externalize for AI to work correctly.</p><p>The failure mode it addresses is drift: architecturally incoherent output generated at AI speed, propagating across every session that inherits the corrupted context. Every person I&#8217;ve spoken with about this in the last year has seen it. The discipline that addresses it is available now.</p><p>Paper: https://doi.org/10.5281/zenodo.19073543</p><p>If you&#8217;re building with AI-assisted tools and the architecture is getting harder to control &#8212; not easier &#8212; this is the argument that explains why. The methodology is documented, the experiments are reproducible, and the tool that implements it is open source.</p><p>Start at genspec.dev (https://genspec.dev).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ambientengineer.dev/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>