<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chan Meng</title>
    <description>The latest articles on DEV Community by Chan Meng (@chan_meng_ba4481817e6694f).</description>
    <link>https://dev.to/chan_meng_ba4481817e6694f</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4230322%2F936a03db-f42c-46e8-82a6-604964b081ee.png</url>
      <title>DEV Community: Chan Meng</title>
      <link>https://dev.to/chan_meng_ba4481817e6694f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chan_meng_ba4481817e6694f"/>
    <language>en</language>
    <item>
      <title>Reproducible is not correct: defect classes a green test suite cannot see</title>
      <dc:creator>Chan Meng</dc:creator>
      <pubDate>Fri, 06 Nov 2026 12:25:44 +0000</pubDate>
      <link>https://dev.to/chan_meng_ba4481817e6694f/reproducible-is-not-correct-defect-classes-a-green-test-suite-cannot-see-d87</link>
      <guid>https://dev.to/chan_meng_ba4481817e6694f/reproducible-is-not-correct-defect-classes-a-green-test-suite-cannot-see-d87</guid>
      <description>&lt;h1&gt;
  
  
  Reproducible is not correct
&lt;/h1&gt;

&lt;p&gt;Here is a measurement you can reproduce in eight lines, plus a dozen-line helper. It is from the example plans that ship with my floor-plan compiler — a U-shaped house built around a courtyard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@chanmeng666/archlang&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Ray casting: count crossings of a ray from `point` towards +x. Points on an edge count as inside.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pointInPolygon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;polygon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onEdge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cross&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onEdge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;inside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inside&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;inside&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;examples/courtyard-house.arch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor_polygon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pts, bbox centre inside floor?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;pointInPolygon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor_polygon&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Living 4 pts, bbox centre inside floor? true&lt;/span&gt;
&lt;span class="c1"&gt;// Entry 4 pts, bbox centre inside floor? true&lt;/span&gt;
&lt;span class="c1"&gt;// Dining 4 pts, bbox centre inside floor? true&lt;/span&gt;
&lt;span class="c1"&gt;// Gallery 8 pts, bbox centre inside floor? false   &amp;lt;-- here&lt;/span&gt;
&lt;span class="c1"&gt;// Bedroom 1 4 pts, bbox centre inside floor? true&lt;/span&gt;
&lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gallery is an eight-point room wrapped around a courtyard. Its bounding box has a centre, that centre is a perfectly good coordinate, and it is &lt;strong&gt;outside the room&lt;/strong&gt;. Anything that reached for that point — to place a label, to start a route, to decide which side of a wall is "outside" — got a confident, silent, wrong answer.&lt;/p&gt;

&lt;p&gt;That is not one bug. It is a defect &lt;em&gt;class&lt;/em&gt;, and my project shipped six instances of it before anybody noticed. This post is about the three classes I have actually shipped, and what each one taught me about the difference between a gate that is green and a system that is right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Class 1: a position derived from a shape's box instead of its shape
&lt;/h2&gt;

&lt;p&gt;The six instances, all fixed in one release, all silent while they lived:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the point a room's name and area are drawn at;&lt;/li&gt;
&lt;li&gt;the anchor a circulation route starts from — one plan reported a 10.9 m walk as 5.6 m;&lt;/li&gt;
&lt;li&gt;the witness lines of automatic dimensions, which floated metres off a sloped facade;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;swing into &amp;lt;room&amp;gt;&lt;/code&gt;, which gave a door a swing into a wall the room does not border;&lt;/li&gt;
&lt;li&gt;furniture placed &lt;code&gt;against wall&lt;/code&gt;, which backed a piece onto a wall outside its own room;&lt;/li&gt;
&lt;li&gt;and the compass direction a window faces, which on a courtyard plan was reported &lt;strong&gt;backwards for every window&lt;/strong&gt;, because the code found "outward" by comparing against the midpoint of the building's bounding box, and on a courtyard plan that midpoint sits in the courtyard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of them passed the whole test suite. Every one of them produced plausible output. The linter — the part of the system whose entire job is to say when a plan is unsound — reported none of them, because a wrong label position is not an unsound building, it is a correct building described wrongly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually finds this class:&lt;/strong&gt; not a test, a grep. Search for consumers of &lt;code&gt;room.size&lt;/code&gt; or &lt;code&gt;r.size.w&lt;/code&gt; with no nearby branch on the room's polygon. That pattern is a shape consumer reading the box when the ring is the honest datum. The fix is local and closed-form every time — probe one wall thickness off each face and ask which side has floor — and the general rule is the one sentence I wish I had written three releases earlier: &lt;strong&gt;a derived position comes from the shape, never from its bounding box or centroid.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why a rectangle hides it.&lt;/strong&gt; On a rectangular room the box centre &lt;em&gt;is&lt;/em&gt; inside the room, and every one of those six code paths is correct. The entire class is invisible until a room is L-shaped, U-shaped, polygonal or round. If your fixtures are all rectangles, your suite is measuring the easy half of your input space.&lt;/p&gt;

&lt;h2&gt;
  
  
  Class 2: a generator that reproduces the same wrong text forever
&lt;/h2&gt;

&lt;p&gt;I have a CI gate that regenerates every generated artifact and fails if the committed file differs. Grammars, error-code documentation, CLI reference, JSON schemas, a one-page language spec. It is a good gate. It has been green for a very long time.&lt;/p&gt;

&lt;p&gt;It is also, on its own, vacuous — and this is the part that took me longest to see.&lt;/p&gt;

&lt;p&gt;The gate compares a generator's &lt;strong&gt;output&lt;/strong&gt; against the committed file. That proves the artifact is &lt;strong&gt;reproducible&lt;/strong&gt;. It says nothing whatsoever about whether the generator is &lt;strong&gt;right&lt;/strong&gt;. A generator that hardcodes a language fact instead of deriving it will reproduce the same wrong text on every run, for every release, with a green check beside it the whole way.&lt;/p&gt;

&lt;p&gt;Both halves of that happened here, and both are in the public changelog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the spec generator shipped a CLI reference three versions out of date, and omitted a keyword entirely, for three releases;&lt;/li&gt;
&lt;li&gt;the grammar generator hardcoded a number regex that did not include the unit suffixes the lexer had accepted for months. The published grammar and the actual parser disagreed about the language, and nothing could see it, because each was internally consistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The rule that came out of it:&lt;/strong&gt; a generator must derive from the source of truth — the same tables the compiler itself reads — and never retype it. And each generator needs a guard that fails when a source-of-truth entry has &lt;strong&gt;no rendering at all&lt;/strong&gt;, because a missing row is exactly what a reflexive gate cannot notice.&lt;/p&gt;

&lt;p&gt;The general form is worth stating on its own, because it is not specific to compilers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A reflexive check is sound for a &lt;strong&gt;derived&lt;/strong&gt; artifact and vacuous for a &lt;strong&gt;retyped&lt;/strong&gt; one, and it cannot tell you which kind it is looking at.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Class 3: a surface no test ever executed
&lt;/h2&gt;

&lt;p&gt;The third class is the cheapest to describe and the most embarrassing to find.&lt;/p&gt;

&lt;p&gt;One release found five of them at once. A watch command that had not actually watched for files since it was written, across roughly two dozen releases. A PDF export that drew none of the wall hatching the SVG export drew. A formatter that rewrote pocket doors as hinged ones — silently changing the plan it was asked to tidy.&lt;/p&gt;

&lt;p&gt;All five were found by &lt;strong&gt;running the surface&lt;/strong&gt;. None of them by reading the code, and none of them by the test suite, which exercised the functions underneath each surface perfectly well and never once invoked the surface itself.&lt;/p&gt;

&lt;p&gt;If you have a CLI, a formatter, a watcher or an exporter that is tested only through its internals, you have this class. The test that would have caught all five is unglamorous: run the actual command, on an actual file, and assert on what comes out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do differently now
&lt;/h2&gt;

&lt;p&gt;Three habits, one per class:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Every new language form ships with a byte-identity law, pinned by a test.&lt;/strong&gt; A plan that does not use the new feature must render, describe and lint exactly as it did before — proved with a hash sweep across every shipped example, not by looking at two drawings. When a golden moves, that is a finding to explain before it is a diff to bless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A green gate is asked what it proves.&lt;/strong&gt; "Reproducible" and "correct" are different words and the gate only ever said the first one. Where a check is reflexive, the second check is a human deriving the artifact from the source of truth by hand, once, and comparing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-rectangular fixtures exist in the suite now.&lt;/strong&gt; The courtyard plan at the top of this post is in the repository precisely because it is the shape that exposes class 1. A test corpus made only of easy inputs is a measurement of the easy inputs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this makes the system correct. It makes a specific family of silent wrong answers loud, which is a smaller claim and the only one I can support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;Save the snippet at the top of this post as &lt;code&gt;check.mjs&lt;/code&gt;, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;bbox-check &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;bbox-check
npm init &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; @chanmeng666/archlang@1.57.0
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/@chanmeng666/archlang/examples &lt;span class="nb"&gt;.&lt;/span&gt;   &lt;span class="c"&gt;# the example plans ship in the package&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; ../check.mjs &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; node check.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which prints, against version 1.57.0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Living 4 pts, bbox centre inside floor? true
Entry 4 pts, bbox centre inside floor? true
Dining 4 pts, bbox centre inside floor? true
Gallery 8 pts, bbox centre inside floor? false
Bedroom 1 4 pts, bbox centre inside floor? true
Bedroom 2 4 pts, bbox centre inside floor? true
Bedroom 3 4 pts, bbox centre inside floor? true
Study 4 pts, bbox centre inside floor? true
Kitchen 4 pts, bbox centre inside floor? true
Bathroom 4 pts, bbox centre inside floor? true
Utility 4 pts, bbox centre inside floor? true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler is MIT, has zero runtime dependencies, and compiles floor plans written as text to SVG, DXF and PDF: &lt;a href="https://archlang.uk" rel="noopener noreferrer"&gt;https://archlang.uk&lt;/a&gt;. There is a playground if you would rather not install anything: &lt;a href="https://playground.archlang.uk" rel="noopener noreferrer"&gt;https://playground.archlang.uk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have shipped one of these three, I would genuinely like to read about it. The first class in particular seems like something everyone with geometry in their system hits exactly once.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>typescript</category>
      <category>programming</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Floor plans that compile: byte-identical output, errors as data, and an access graph</title>
      <dc:creator>Chan Meng</dc:creator>
      <pubDate>Fri, 06 Nov 2026 12:24:42 +0000</pubDate>
      <link>https://dev.to/chan_meng_ba4481817e6694f/floor-plans-that-compile-byte-identical-output-errors-as-data-and-an-access-graph-cd8</link>
      <guid>https://dev.to/chan_meng_ba4481817e6694f/floor-plans-that-compile-byte-identical-output-errors-as-data-and-an-access-graph-cd8</guid>
      <description>&lt;h1&gt;
  
  
  Floor plans that compile
&lt;/h1&gt;

&lt;p&gt;I compiled the same floor plan twice and diffed the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readFileSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@chanmeng666/archlang&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;laneway-house.arch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;byteLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// 40104 true a44e7ed5d5bb1448&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical. Not "visually identical" — the same 40,104 bytes, the same hash. Across the 37 example plans that ship with the project, 35 recompile byte-identically; the other two &lt;code&gt;import&lt;/code&gt; from other files and produced no bytes at all in this in-memory harness, which is a missing file loader rather than a difference in output. Every output quoted in this post is from version 1.57.0 of the package.&lt;/p&gt;

&lt;p&gt;That property is not a party trick. It is the thing that makes everything else in this post possible, and it is the thing an image generator cannot give you at any price.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with a picture
&lt;/h2&gt;

&lt;p&gt;Ask a model for a floor plan today and you get an image. An image has one interface: look at it. You cannot diff it, you cannot ask it whether the bathroom is reachable, and when you say "make the bedroom a metre wider" you get a new image where the kitchen also moved, for reasons nobody can name.&lt;/p&gt;

&lt;p&gt;ArchLang takes the other road. You write source (trimmed here from the shipped &lt;code&gt;laneway-house.arch&lt;/code&gt; example, which adds a bath partition, two more doors, two more windows and the furniture):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plan "Laneway House" {
  units mm
  grid 50
  north up
  dims auto all
  site { street north }

  wall id=w_lane   exterior  thickness 200 { (0,0) (7500,0) }
  wall id=w_east   exterior  thickness 200 { (7500,0) (7500,6500) }
  wall id=w_garden exterior  thickness 200 { (0,6500) (7500,6500) }
  wall id=w_west   exterior  thickness 200 { (0,0) (0,6500) }
  wall id=w_hall   partition thickness 100 { (4200,0) (4200,6500) }

  room id=r_live at (0,0) size 4200x6500 label "Living / Kitchen" uses living kitchen
  strip down at (4200,0) gap 0 width 3300 {
    room id=r_bath size 2500 label "Bath"    uses bath
    room id=r_bed  size 4000 label "Bedroom" uses bedroom
  }

  door id=d_front on w_lane at 2100 width 900 hinge near start swing into r_live
  door id=d_bath  pocket on w_hall at 900 width 800 slide right

  window on w_west   at 2400 width 1200
  window on w_garden at 700  width 1000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coordinates are integer millimetres. Nothing places a door by eye: an opening is pinned to a distance along a &lt;em&gt;named wall&lt;/em&gt;, so moving the wall moves the door. "Make the bedroom a metre wider" is one number, and the diff says so.&lt;/p&gt;

&lt;p&gt;That compiles to SVG. Also DXF, PDF, PNG, and an ASCII plan for a terminal. Zero runtime dependencies; the whole compiler is TypeScript and runs in a browser as happily as in Node, which is why the playground has no server to send your plan to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading a plan back, without looking at it
&lt;/h2&gt;

&lt;p&gt;Here is the part I care about most. &lt;code&gt;describe()&lt;/code&gt; turns the compiled plan into facts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@chanmeng666/archlang&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// "Laneway House" — a 3-room floor plan, 48.75 m² total: Living / Kitchen (27.3 m²),&lt;/span&gt;
&lt;span class="c1"&gt;// Bath (8.25 m²), Bedroom (13.2 m²); 4 doors, 4 windows, entrances via d_front, d_garden.&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   rooms: 3,&lt;/span&gt;
&lt;span class="c1"&gt;//   doors: 4,&lt;/span&gt;
&lt;span class="c1"&gt;//   windows: 4,&lt;/span&gt;
&lt;span class="c1"&gt;//   floor_area_m2: 48.75,&lt;/span&gt;
&lt;span class="c1"&gt;//   gross_internal_area_m2: 45.99,&lt;/span&gt;
&lt;span class="c1"&gt;//   gross_internal_area_all_levels_m2: 45.99,&lt;/span&gt;
&lt;span class="c1"&gt;//   net_floor_area_m2: 45.05&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entrances&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// [ 'd_front', 'd_garden' ]&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;adjacent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// [ 'r_bath', 'r_bed' ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those areas are shoelace arithmetic over the room polygon, not an estimate off a raster. The access graph is a real modeled graph of which door connects which pair of spaces, so "is the bathroom reachable from the front door" is a query rather than a squint.&lt;/p&gt;

&lt;p&gt;A text-only model can call this. It never has to render anything to find out whether it built what it was asked for — which is the whole reason this language exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors are data, and some of them carry the fix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;compile()&lt;/code&gt; never throws. A broken plan comes back as a result with diagnostics, each carrying a byte span:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;broken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`plan "Demo" {
  units mm
  wall id=w1 exterior thickness 200 { (0,0) (5000,0) (5000,4000) (0,4000) close }
  room id=r1 at (0,0) size 5000x4000 label "Room"
  door id=d1 on w1 at 30000 width 900
}`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;broken&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"E_ATTACH_POS_RANGE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Door &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;d1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; attachment position 30000 mm is outside the wall run (0…18000)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"span"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;170&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fixes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clamp the attachment position to 18000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"applicability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"machine-applicable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fixId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"attach-pos-range"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"edits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"span"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;170&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"newText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"on w1 at 18000"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;applicability: "machine-applicable"&lt;/code&gt; is a promise: the edit can be applied without asking anyone. &lt;code&gt;arch fix&lt;/code&gt; walks the diagnostics, applies those edits to a fixpoint, and prints the unified diff it would write. An agent's correction loop is three commands and no image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @chanmeng666/archlang compile plan.arch &lt;span class="nt"&gt;--json&lt;/span&gt;   &lt;span class="c"&gt;# { ok, diagnostics, summary }&lt;/span&gt;
npx @chanmeng666/archlang fix plan.arch &lt;span class="nt"&gt;--dry-run&lt;/span&gt;    &lt;span class="c"&gt;# the exact diff it would apply&lt;/span&gt;
npx @chanmeng666/archlang describe plan.arch &lt;span class="nt"&gt;--json&lt;/span&gt;  &lt;span class="c"&gt;# did I build what was asked?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every command takes &lt;code&gt;--json&lt;/code&gt; and exits &lt;code&gt;0&lt;/code&gt; ok, &lt;code&gt;2&lt;/code&gt; user-source error, &lt;code&gt;1&lt;/code&gt; IO, &lt;code&gt;3&lt;/code&gt; bad usage. A typo earns the &lt;code&gt;3&lt;/code&gt;: &lt;code&gt;--jsn&lt;/code&gt; gets &lt;code&gt;did you mean --json?&lt;/code&gt; rather than being read as a filename.&lt;/p&gt;

&lt;h2&gt;
  
  
  Soundness is a separate layer
&lt;/h2&gt;

&lt;p&gt;Compiling is not the same as being a sensible building. &lt;code&gt;lint()&lt;/code&gt; is the layer that says so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lint&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@chanmeng666/archlang&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twoRoomsNoDoors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`plan "Two rooms" {
  units mm
  wall id=w1 exterior thickness 200 { (0,0) (6000,0) (6000,4000) (0,4000) close }
  wall id=w2 partition thickness 100 { (3500,0) (3500,4000) }
  room id=r_k at (0,0) size 3500x4000 label "Kitchen" uses kitchen
  room id=r_b at (3500,0) size 2500x4000 label "Bath" uses bath
}`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;lint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;twoRoomsNoDoors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_ROOM_DISCONNECTED: Room \"Kitchen\" has no door or opening — it can't be entered.",&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_ROOM_NO_FIXTURE: Kitchen \"Kitchen\" has no fixtures (sink, counter, stove…).",&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_ROOM_DISCONNECTED: Room \"Bath\" has no door or opening — it can't be entered.",&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_ROOM_NO_FIXTURE: Bathroom \"Bath\" has no fixtures (WC, basin, shower…).",&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_NO_ENTRANCE: The plan has no exterior door or opening — there is no way into the building.",&lt;/span&gt;
&lt;span class="c1"&gt;//   "W_HABITABLE_NO_WINDOW: Kitchen \"Kitchen\" (14 m²) has no window on any of its walls; a room people live in needs daylight."&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That plan compiles perfectly. It is also a building with two rooms you cannot enter. Separating "is it valid" from "is it sound" is a deliberate split, and it is why &lt;code&gt;lint&lt;/code&gt; has its own exit behaviour and its own catalogue of codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am not claiming
&lt;/h2&gt;

&lt;p&gt;Three things in this system are measurements, and I will defend them: &lt;strong&gt;reachability&lt;/strong&gt; (a modeled access graph), &lt;strong&gt;exact areas&lt;/strong&gt; (shoelace arithmetic, or πR² on a round room), and &lt;strong&gt;byte-identical determinism&lt;/strong&gt; (the hash at the top of this post).&lt;/p&gt;

&lt;p&gt;Everything else is advisory and the code says so. Clear widths are a nominal width with a constant allowance subtracted. Walk distances come off a coarse quantised grid. The project's own design records call those numbers deliberately coarse and refuse to dress them up, and I would rather tell you which numbers to trust than let you assume all of them.&lt;/p&gt;

&lt;p&gt;Some other limits, stated before you find them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It draws a plan, and only a plan.&lt;/strong&gt; Heights exist in the language but draw nothing, because a floor plan is a horizontal cut. No sections, no elevations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No BIM, no IFC, no round-trip.&lt;/strong&gt; DXF export is one-way, for a draftsperson to open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No structural, no MEP, no code compliance&lt;/strong&gt; — the last one is a refusal rather than a gap. Asserting standards compliance is a liability claim this project will not make.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A concept tool, not a permit set.&lt;/strong&gt; Dimensioned, consistent, exportable to real CAD. Not stamped construction documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo maintainer, MIT.&lt;/strong&gt; Bus factor of one. The licence is chosen so the format outlives me if it is worth keeping.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Playground, compiles in your browser, no signup: &lt;a href="https://playground.archlang.uk" rel="noopener noreferrer"&gt;https://playground.archlang.uk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://archlang.uk" rel="noopener noreferrer"&gt;https://archlang.uk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source (MIT): &lt;a href="https://github.com/ChanMeng666/archlang" rel="noopener noreferrer"&gt;https://github.com/ChanMeng666/archlang&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx @chanmeng666/archlang new -o plan.arch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MCP server, if your agent speaks it: &lt;code&gt;npx -y @chanmeng666/archlang-mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something with it, or break it, I would like to know either way.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>showdev</category>
      <category>compilers</category>
    </item>
  </channel>
</rss>
