{"id":4762,"date":"2026-08-30T00:43:48","date_gmt":"2026-08-30T03:43:48","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/08\/30\/the-undo-has-to-exist-before-the-write-does\/"},"modified":"2026-08-30T00:43:48","modified_gmt":"2026-08-30T03:43:48","slug":"the-undo-has-to-exist-before-the-write-does","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/08\/30\/the-undo-has-to-exist-before-the-write-does\/","title":{"rendered":"The undo has to exist before the write does"},"content":{"rendered":"<div>\n<div><\/div>\n<p>Three arms, no fourth. The design decision worth arguing about is what&#8217;s excluded: an evaluation that could not be performed is not an arm. It&#8217;s an error, returned outside the verdict type altogether.<\/p>\n<p>The usual formulation is &#8220;three-valued logic, ALLOW \/ DENY \/ UNKNOWN&#8221;, and it quietly merges two different unknowns. We evaluated and couldn&#8217;t decide is a decision. We couldn&#8217;t evaluate is the absence of one. Fold the second into the first and a caller reading an error as a denial has made a policy choice on the engine&#8217;s behalf. Fail-closed belongs in the caller&#8217;s policy, not in the type.<\/p>\n<p>I care about this more than about the rest of the design, because I broke it myself. My own audit tooling collapsed &#8220;couldn&#8217;t measure&#8221; into &#8220;measured false&#8221; and misreported 18 of 65 items on 2026-08-29. Loosening the probe made it under-report, tightening it made it over-report, and the threshold was never the answer; the third value was. Loading &#8220;couldn&#8217;t measure&#8221; into &#8220;measured false&#8221; is a measurement failure being reported as a property of the subject, and it&#8217;s the same defect whichever direction it points.<\/p>\n<p>Two evaluation systems run, a policy evaluator and an invariant registry, and their results meet with Deny absorbing across all four quadrants. Reasons are held in a canonical order, so two evaluations refusing for the same reasons produce the same proof digest regardless of which system got consulted first.<\/p>\n<h2> <a name=\"refusals-get-receipts-too\" href=\"#refusals-get-receipts-too\"> <\/a> Refusals get receipts too <\/h2>\n<p>A receipt is signed with Ed25519, wrapped in a DSSE envelope, and its leaf goes into a Merkle log with leaf and interior domains separated: <code>H(0x00 || leaf)<\/code> against <code>H(0x01 || left || right)<\/code>, following the RFC 6962 section 2.1 construction with RFC 9162&#8217;s numbered steps for inclusion and consistency proofs. The separation is what stops a leaf being presented as an interior node.<\/p>\n<p>What a receipt asserts: this change was judged before it was applied; where an inverse could be constructed it was constructed, checked and stored first; the verdict was recorded, refusals included; the canonical identifier is consistent.<\/p>\n<p>What it does not assert: that your predicate expresses anyone&#8217;s intent, that a change satisfying it is safe, or that anybody can reconstruct the data that changed. Object bytes aren&#8217;t stored. And a clean verdict count proves only that nothing was hidden from the counter. Weaken the policy until it admits everything and the count still reads clean.<\/p>\n<p>The receipt also carries a field naming where replay-determinism stops: <code>deterministic_replay<\/code>, <code>llm_originated<\/code>, <code>mixed<\/code>, <code>unknown<\/code>. <code>Mixed<\/code> isn&#8217;t an atomic fifth value, it carries two stage sub-fields drawn from a three-valued set of their own, so the unconstrained carrier is twelve inhabitants and four is just the count of top-level shapes. The combining function refuses to mint <code>unknown<\/code> over a stage that already has an established class. A stage nobody established can be said out loud. A stage that was established can&#8217;t be laundered into silence. That node is the one place in the whole design where a written spec clause, a running implementation and a test that enforces it all exist at the same time, which is why I keep pointing at it.<\/p>\n<h2> <a name=\"what-actually-runs\" href=\"#what-actually-runs\"> <\/a> What actually runs <\/h2>\n<p>Splitting this out, because everything above reads like a description of software and about a third of it isn&#8217;t.<\/p>\n<p>Nineteen of 27 components are running code, one of those partial. Three are designed with a written contract and not built. Five are targets with no contract at all. At layer granularity that&#8217;s 6 of 9, and the two with zero implementation are the search layer and the evolution layer. Where the paper describes those, it&#8217;s describing something I want.<\/p>\n<p>Measured on 2026-08-29 unless noted: 17 workspace crates, of which 13 were shipped publicly as of 2026-08-25. Three refusal reason codes against a vision that names eight law classes. Eighteen receipt payload fields. A seven-method substrate contract with a conformance harness every adapter has to pass. Five substrate families, being filesystem, Git, tool-protocol proxy, PostgreSQL and MySQL, and the MySQL adapter has never been run against a live instance. The Lean model carries 117 theorems, 12 of them named counterexamples, 1 carried axiom, 0 <code>sorry<\/code>, re-counted on a fresh clone on 2026-08-26. Public test floor: 2,664 probes across 470 suites as of 2026-08-30, reconstructed from the tree rather than re-measured on a clean clone. That floor has moved more than forty times in a month, which tells you what a single reading of it is worth.<\/p>\n<p>Then the condition that governs every number above. CI has run zero jobs on any push since 2026-08-15T17:25:29Z. Thirteen days, 2,245 commits, no machine signal, because the account is billing-blocked rather than because of a code defect. Failing runs show two to five seconds of &#8220;duration&#8221;, which is the scheduler refusing the job before assignment, and there&#8217;s no log for any of them because none of them ever ran. The workflow that cuts a release has never run at all. So every figure here is a local or hand-taken measurement, and &#8220;it compiles clean&#8221; is not the claim a green build would have been.<\/p>\n<p>One more, since it&#8217;s the kind of thing that usually gets found rather than disclosed. A frozen test in the repo reproduces a single signed receipt carrying <code>inverse_delta = None<\/code> while <code>reversibility = Some(true)<\/code>. Two fields on one signed record disagreeing about whether the change could be put back. It&#8217;s marked <code>#[ignore]<\/code>, it isn&#8217;t repaired, and whether the fix needs a fourth value in that vocabulary is still open.<\/p>\n<h2> <a name=\"nothing-here-is-new-and-thats-deliberate\" href=\"#nothing-here-is-new-and-thats-deliberate\"> <\/a> Nothing here is new, and that&#8217;s deliberate <\/h2>\n<p>Every part has a named ancestor. Leaf and interior domain separation in a Merkle tree is Certificate Transparency. The signed envelope is DSSE, out of in-toto. Content addressing over a canonical encoding is IPLD, and Git before it. Compare-and-swap on a precondition fingerprint is optimistic concurrency control. Pre-provisioned inverses are compensating transactions, which is Sagas, 1987. Deciding before applying, then applying exactly the thing that was decided about, is the plan\/apply idiom. The policy engine is an unmodified third-party evaluator.<\/p>\n<p>What&#8217;s offered is the combination: those parts constraining each other in one pipeline, under one vocabulary that reaches from the substrate contract up to the drawn surface, with the coverage boundary published as a first-class artifact instead of an appendix. Where I say I didn&#8217;t find something assembled this way, that&#8217;s a statement about how I searched, carrying a date and a method. It isn&#8217;t a statement about the world. No mechanism here gets called a first, the only one of its kind, or unique.<\/p>\n<h2> <a name=\"if-you-want-to-break-it\" href=\"#if-you-want-to-break-it\"> <\/a> If you want to break it <\/h2>\n<p>Clone it, run the verifier against a frozen receipt with the network off, and tell me if it passes when it shouldn&#8217;t. Or find a limit that isn&#8217;t already on the limits page, which is honestly the reply I want, because that one turns into inventory.<\/p>\n<p>Repo: <a href=\"https:\/\/github.com\/TraceFold\/tracefold\" target=\"_blank\" rel=\"noopener noreferrer\">github.com\/TraceFold\/tracefold<\/a> (Rust, Apache-2.0).<\/p>\n<p>Not released.<\/p>\n<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/mahirhir\/the-undo-has-to-exist-before-the-write-does-46on\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Three arms, no fourth. The design decision worth arguing about is what&#8217;s excluded: an evaluation that could not be performed is not an arm. It&#8217;s an error, returned outside the verdict type altogether. The usual formulation is &#8220;three-valued logic, ALLOW \/ DENY \/ UNKNOWN&#8221;, and it quietly merges two different unknowns. We evaluated and couldn&#8217;t [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4761,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"webixso_pending_account_ids":""},"categories":[41],"tags":[],"class_list":["post-4762","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/4762","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=4762"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/4762\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/4761"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=4762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=4762"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=4762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}