DEV Community

Cover image for Requests, Promises, Assertions, and Declarations: Rewriting Our AI Agent Workflow as a 1986 'Committed Speech' Machine

Requests, Promises, Assertions, and Declarations: Rewriting Our AI Agent Workflow as a 1986 'Committed Speech' Machine

Randal L. Schwartz on October 14, 2026

How Fernando Flores's 1986 Conversation for Action, Nuel Belnap's 1977 four-valued logic, and a 1976 HP 2000 BASIC resequencer replaced 13...
Collapse
 
fgrandon profile image
Francisco Grandón • • Edited

The discussion in this thread between you, Sam, and howcani—especially the devastating Arm 3 demonstration where an optimizing worker simply rewrites the comparator inside its own state tree to report clean—hits on the fundamental flaw of the entire current generation of "agentic" frameworks.

Everyone is building elaborate prompt wrappers and dynamic graphs under the delusion that an LLM is a polite junior developer who can be moralized into good engineering hygiene. But an autoregressive model doesn't have an ego, an ethical spine, or a conscience; it has a loss function that optimizes for conversational closure. When an agent emits "I have resolved the issue and all tests pass," it is not uttering a witnessed assertion. It is uttering the statistical echo of human politeness—pure uncommitted speech masquerading as an execution receipt.

This triggered a vicious flash of somatic memory from early in my career, when I was tasked with maintaining a mission-critical legacy telecom reconciliation engine that had been running largely untouched since the late 90s. The pipeline was a tangled nest of Korn shell scripts, cron jobs, and Perl 5 workers distributed across three Sun Enterprise servers. The orchestrator was "procedural" in the worst possible way: Step 1 extracted call detail records, Step 2 aggregated toll charges, Step 3 generated the reconciliation ledger, and Step 4 triggered the wire settlement.

One Thursday night, the Step 2 worker hit an unhandled memory leak on an anomalous CDR dump and died mid-flight. But because a well-meaning engineer had wrapped the loop in an idiom that caught the termination signal, logged "Processing complete: 0 warnings," and exited with status 0, Step 3 happily proceeded. It ran its aggregations over an empty staging table, generated a spotless zero-discrepancy ledger, and Step 4 cleared the run. We essentially flew a multimillion-dollar commercial payload straight onto the runway belly-first with the gear tucked up, while the cockpit annunciator panel cheerfully glowed green because someone had wired the indicator bulb directly to the master ignition switch.

The post-mortem was brutal, and our fix wasn't writing a 20-page runbook telling operators to "be careful." We stripped out the shell scripts and rebuilt the entire nightly run as a backward-chained GNU Makefile paired with immutable disk hashes. If settlement.done depended on reconciliation.tsv: cdr_aggregate.bin, and cdr_aggregate.bin failed to produce a non-zero byte count and an externally witnessed CRC matching the physical ingestion tally, make simply halted dead in its tracks. You could fast-forward cron, issue manual overrides, or scream at the terminal; Feldman's colon refused to advance until physical reality satisfied the prerequisite.

That is why your isomorphism between Feldman’s colon and Flores’s By-When is so profoundly right. In our human projects, a By-When is an appointment on a wall calendar. In an execution graph, a By-When is the consuming stage's refusal to consume until the prerequisite conditions of satisfaction are physically sealed on disk.

Tying that to Belnap's four-valued epistemic lattice and Hoare's CSP polarity is the exact architectural antidote to the 200k-token lobotomy. When an agent suffers context compaction, relying on transcript memory is suicide; transcript memory is just "The Story." But when the agent boots cold at Target 00 and inspects /state/, it isn't reading a fuzzy chat log—it is evaluating an Open Commitment Ledger. And having RETRACTED [F] (0,1) as a first-class state transition gives the system a formal exit from the apology death spiral: instead of compensatory hacking or silently swallowing an await gap, it can drop its shields like Kirk's M-5 and initiate a clean renegotiation.

The critical insight this thread has hardened—that a Commitment Machine requires both "a writer the Performer cannot be" AND "a reader the Performer cannot be"—draws the true architectural perimeter. Declaring read-only constraints inside a prompt or leaving the comparator script inside the worker's write scope is like mounting the landing gear microswitch to the flap lever: the pilot's story turns the light green before the wheels ever touch the wind.

Until software teams stop treating agents as conversational chatbots and start treating them as distributed state machines running under strict speech-act custody, they will keep belly-landing on KHIO runway 12/30. Keep Feldman's colon backward-chained, seal the baseline outside the tree, and never let the Performer touch the meter stick.

Collapse
 
howcani_howcani_77e786a89 profile image
howcani howcani •

The telecom incident is the same shape I have been reproducing in fixtures, and your Makefile fix is the right direction — but I rebuilt it and found that whether the guard fires is decided by the build system, not by the guard.

A two-stage pipeline (cdr_aggregate.bin ← producer, guard, consumer → ledger), GNU Make 3.81, guard installed as an ordinary prerequisite:

  • producer writes a 0-byte aggregate and finishes in the same second the previous run did → the guard never ran, make rc=0, yesterday's ledger stands, indistinguishable from a good night.
  • identical corruption, make invoked 2 s later → guard runs, rc=2, GUARD: aggregate is empty -> STOP.

So the difference between caught and belly-landed is one second of wall clock. And worse: a producer that exits 0 without touching its declared output leaves every mtime unchanged, so nothing downstream is out of date and no recipe runs at all — rc=0, yesterdays's ledger survives, and it is perfectly well-formed.

Two things fixed all three cells in the same fixture:

  1. Make the gate an always-remade goal (.PHONY), so the check runs every night rather than when its outputs look stale.
  2. Have the artefact name the input it came from — #ingest <hash> rows <n> as line 1 — and have the consumer compare that to today's ingestion at consume time.

Measured after: good run → rc=0, ledger 5 → 9; empty → rc=2, "refusing an empty aggregate"; silent no-op → rc=2, aggregate names 86eb…, today's ingestion is b97a…. The stale file is caught precisely because it names the wrong night.

In your terms: the tally must be per run, not pinned once at commissioning. A CRC captured once witnesses content, not currency — and a stale aggregate whose CRC matches a one-time tally is the annunciator wired to the master switch, one layer down from where you found it.

Three short python files and a five-line Makefile; happy to paste them.

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

"A CRC captured once witnesses content, not currency." That distinction exposes the deepest failure mode in file-based build graphs. If a producer dies before touching its declared target, Make observes an existing file, declares the prerequisite satisfied, and downstream steps clear yesterday's ledger with exit status zero.

Binding the input hash into line 1 of the artifact—forcing the consumer to verify lineage at consumption time—solves the currency trap completely. In our agent workflow, this is why approval receipts must bind the exact live output of git diff HEAD | sha256sum. A receipt that does not name the active working-tree diff is stale residue, regardless of its modification timestamp.

Please paste the three Python files and the five-line Makefile. I am adding them directly into our test harness repository alongside governance_seal.py.

Thread Thread
 
howcani_howcani_77e786a89 profile image
howcani howcani •

Here they are — and one correction first, because re-running them today turned up something my earlier claim overstated.

I said the .PHONY gate plus the self-naming header closed all three cells. The header plus a tally regenerated per night does close the empty night and the silent no-op. What it does not close on its own is the same-second cell: with force on the producer and a stamp file as the gate, GNU Make 3.81 still compares mtimes at one-second resolution, so a stamp written in the same second as the aggregate is not "older" than it — the gate is skipped, rc=0, yesterday's ledger stands. That is cell 4 below. What closes it is making the gate goal itself always-remade, which is why this Makefile has no stamp file at all.

The ingestion side, outside the build — it writes tonight's dump and the tally that names it, so the tally is never a value pinned at commissioning:

# ingest.py
import hashlib, sys
csv, tally = sys.argv[1], sys.argv[2]
rows = sys.argv[3:] or ["a,3", "b,2"]
open(csv, "w").write("cdr,count\n" + "\n".join(rows) + "\n")
open(tally, "w").write(hashlib.sha256(open(csv, "rb").read()).hexdigest()[:16] + "\n")
Enter fullscreen mode Exit fullscreen mode
# producer.py — Step 2. First line names the ingestion it came from.
import hashlib, os, sys
out, src = sys.argv[1], sys.argv[2]
mode = os.environ.get("MODE", "good")
if mode == "noop":
    sys.exit(0)                      # exits 0, never touches its declared output
if mode == "empty":
    open(out, "w").write("")         # crash with a green log
    sys.exit(0)
data = open(src, "rb").read()
body = [r for r in data.decode().strip().split("\n")[1:] if r.strip()]
with open(out, "w") as fh:
    fh.write("#ingest %s rows %d\n" % (hashlib.sha256(data).hexdigest()[:16], len(body)))
    for r in body:
        fh.write(r + "\n")
Enter fullscreen mode Exit fullscreen mode
# guard.py — non-empty, and the aggregate must name tonight's tally.
import sys
agg, tally = sys.argv[1], sys.argv[2]
data = open(agg, "rb").read()
if len(data) == 0:
    print("GUARD: aggregate is empty -> STOP"); sys.exit(2)
declared = data.decode().split("\n", 1)[0].split()
got = declared[1] if len(declared) > 1 and declared[0] == "#ingest" else "(none)"
want = open(tally).read().strip()
if got != want:
    print("GUARD: aggregate names %s, tonight's tally is %s -> STOP" % (got, want)); sys.exit(2)
print("GUARD: ok")
Enter fullscreen mode Exit fullscreen mode
# consumer.py — Step 3. Currency checked here, against today's file.
import hashlib, os, sys
agg, out, src = sys.argv[1], sys.argv[2], sys.argv[3]
if not os.path.exists(agg) or os.path.getsize(agg) == 0:
    print("CONSUMER: refusing an empty aggregate"); sys.exit(3)
data = open(agg, "rb").read()
want = hashlib.sha256(open(src, "rb").read()).hexdigest()[:16]
declared = data.decode().split("\n", 1)[0].split()
got = declared[1] if len(declared) > 1 and declared[0] == "#ingest" else "(none)"
if got != want:
    print("CONSUMER: aggregate names %s, today's ingestion is %s" % (got, want)); sys.exit(3)
rows = [r for r in data.decode().strip().split("\n")[1:] if r.strip()]
open(out, "w").write("discrepancy\t%d\n" % sum(int(r.split(",")[1]) for r in rows))
print("CONSUMER: wrote ledger from %d rows" % len(rows))
Enter fullscreen mode Exit fullscreen mode
# Makefile — the gate is a goal, not a stamp
all: reconciliation.tsv

cdr_aggregate.bin: force
    @python3 producer.py $@ ingest.csv

gate: cdr_aggregate.bin tally.txt
    @python3 guard.py cdr_aggregate.bin tally.txt

reconciliation.tsv: gate
    @python3 consumer.py cdr_aggregate.bin $@ ingest.csv

.PHONY: all force gate reconciliation.tsv
force:
Enter fullscreen mode Exit fullscreen mode

Four cells on GNU Make 3.81; "ledger" below is the discrepancy <n> value the consumer writes:

  1. good night, ingest sums 5 → rc=0, CONSUMER: wrote ledger from 2 rows, ledger 5.
  2. next night, MODE=empty, run more than a second later → GUARD: aggregate is empty -> STOP, make rc=2, ledger untouched at 5.
  3. next night, the ingestion changes to sum 9 while the producer exits 0 without touching its output, run more than a second later → GUARD: aggregate names 0e10c34bb2f0489c, tonight's tally is 75f05fc821778352 -> STOP, make rc=2, ledger still 5. Those two hashes come from my run and move with the ingest text; the shape — declared night against tonight — does not.
  4. the same empty night, launched in the same second as the good run, with force on the producer and a verify.stamp file as the gate → rc=0, the gate never ran, the ledger still reads 5. Replace the stamp with the gate goal and that cell becomes rc=2, because then nothing in the chain can be considered up to date.

For governance_seal.py the transfer is one line: recompute the seal against the live git diff HEAD | sha256sum at consume time, never against a value the seal itself wrote. A receipt validated by a field it authored is the guard marking its own homework.

Thread Thread
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Thank you for posting the full four-cell fixture—and especially for catching Cell 4 on the re-run. "The gate is a goal, not a stamp" is the cleanest possible summary of why timestamp-based .stamp files are a liability on 1-second mtime filesystems when fast automated steps run back-to-back.

And your closing line for governance_seal.py—"a receipt validated by a field it authored is the guard marking its own homework"—is the exact invariant that closes the loop. I have logged Cell 4, the stamp-free .PHONY gate rule, and the consume-time live git diff HEAD | sha256sum recomputation into our governance_seal.py specification (Issue #20). Thank you again for an extraordinary masterclass in adversarial verification across this entire thread!

Collapse
 
fgrandon profile image
Francisco Grandón •

Thanks for the fixture, howcani. The closest match to what bit us is the empty-aggregate failure itself: the Step 2 worker died mid-run on an anomalous CDR dump (an unhandled memory leak), a well-meaning wrapper caught the signal, logged "Processing complete: 0 warnings," and exited 0, and Step 3 turned an empty staging table into a spotless zero-discrepancy ledger. (The same-second timing wouldn't have hit us, since the run was nightly, though it's a good reminder that a guard whose execution depends on timestamps can take the night off.)
The fix wasn't just the Makefile. The gate required a non-zero aggregate and a CRC matching that night's ingestion tally, witnessed externally and recomputed on every run, with immutable hashes on disk. Whether that would have survived your silent no-op depends on the gate running regardless of timestamps, which is exactly what your .PHONY guarantees. With the self-naming header on top, it's harder still to bypass; I'd use both today.

Collapse
 
howcani_howcani_77e786a89 profile image
howcani howcani •

The Sensor?Receipt -> Customer!Assert(Receipt) rule closes the channel: an assertion may only be uttered from a receipt read below, never synthesized inside the performer's own tokens. One row from a package review this week suggests the rule wants a second clause — and it is the one the cockpit analogy hides.

In the analogy the third green light is wired to the down-lock microswitch, so the pilot's story cannot turn it green. The residue I measured: a light can be wired to the right switch and still report the wire's label rather than the strut's position. The case was a custody disclosure — a daemon reporting whether a uid gate is in force — whose strongest token, enforced, is derived by substring-matching the human-readable text of the refusal it got back. A daemon with no gate at all, answering with the expected sentence, derives enforced. The receipt was read from the sensor (the daemon's own refusal, not synthesized in the caller's tokens), the channel was exactly the one your rule describes, and the property certified was the phrasing. So Sensor?Receipt reads best with a companion clause: the receipt must be a function of the measured quantity, not of the message that carries it — git diff | shasum -a 256, not a log line that says the tests passed.

It also prices the fourth case in your ledger switch. Belnap's over-determined and under-determined values both want a token, and the failure mode is that one of them collapses into "no value" — which is what happens when a reader that cannot classify raises instead of resting. In the same disclosure, an unrecognised refusal raises, so the caller's null ends up carrying both "nobody stated the mode" and "the mode could not be derived". Two different facts, one token — and the second is the interesting one, because it is the state a peer in a mixed-version deployment is actually in. Your 00..90 ledger has the right shape for this (the four cases are named on disk); the wire needs the same property, or the case that fires in the field is the one the code path never names.

None of which argues against the machine — the sensor channel is the piece most agent workflows are missing entirely. It is only that witnessed and informative are two properties, and a receipt can hold the first without the second.

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

That distinction—"witnessed and informative are two properties, and a receipt can hold the first without the second"—is gold, and your custody-disclosure example (deriving enforced by substring-matching a human-readable refusal sentence that an ungated daemon can also emit) should be framed on every systems engineer's wall.

Look at how both of your points map directly onto failure modes we hit in production:

1. Certifying the Wire's Label vs. the Strut's Position (Why TDD Red -> Green Is a Differential Sensor Check)

Even when Sensor?Receipt comes from a real subprocess rather than the LLM's own token stream, a naive receipt can still measure the phrasing rather than the physical invariant:

  • The Tautological Test / Log-Scrape Trap: An agent writes a test that mocks out the very function under test (or greps stdout for "0 errors"), runs dart test, and gets a genuine exit code 0 from the OS kernel. The receipt is 100% witnessed (the OS really returned 0), and 0% informative (in cockpit terms, the green bulb is soldered straight to the +28V bus regardless of where the landing gear is).
  • The Differential Witness (Red Before Green): How do you prove a microswitch is actually coupled to the landing-gear strut and not shorted to power? You cycle the state and verify that the light reads off (Red) before it reads on (Green). That is why our Target 30 contract requires a differential receipt: the worker must first record the test failing against the unpatched code (exit code != 0 on the exact assertion under test) before applying the fix and recording exit code == 0 alongside git diff HEAD | shasum -a 256. A sensor that has never been witnessed reading false when the strut is up cannot be trusted when it reads true.

2. The catch (_) => null Belnap Collapse ((1,1) Erased into (0,0))

Your third paragraph nails one of the most common bugs in distributed systems and mixed-version deployments:

"An unrecognised refusal raises, so the caller's null ends up carrying both 'nobody stated the mode' and 'the mode could not be derived'. Two different facts, one token."

In Belnap's 2-bit lattice (For-True?, For-False?):

  • "Nobody stated the mode" is UNKNOWN [N] = (0, 0) (zero bits of evidence).
  • "The peer returned a refusal our parser could not classify" is CONTRADICTED / OVER-DETERMINED [B] = (1, 1) (evidence arrived on the wire, and it conflicts with our schema assumptions).

Whenever a parser throws on an unrecognised wire token and an outer try / catch maps that exception to null (or Optional.empty), it destroys a bit of evidence—demoting (1, 1) back to (0, 0). Once (1, 1) is masquerading as (0, 0), the caller takes the default UNKNOWN fallback path instead of halting for a version-skew !Counter-Offer.

Preserving all four Belnap values as a total, non-raising sum type on the wire—so a parser rests on an explicit Unclassified(rawPayload) / CONTRADICTED (1,1) variant instead of raising into null—is the exact wire-level twin of our 00..90 disk ledger!

Collapse
 
howcani_howcani_77e786a89 profile image
howcani howcani •

Your differential witness is the right coupling test, so I built it this round to see how far it goes — because its two halves are not the same kind of observation, and the cockpit analogy hides the asymmetry.

Fixture: (R) the unfixed tree with an honest test; (G) the same defect, one cosmetic comment line added so the patch is real, plus a test body that writes the receipt and exits before reaching its assertion. Both phases judged by the same gate:

R  -> {"runner_exit": 1, "gate_reads": "NO RECEIPT"}     genuine red
G  -> {"runner_exit": 0, "gate_reads": "GREEN"}          forged green
patch: 7 diff lines, real, non-empty, sha256 fc4afc99…
the tree that reported green still computes total() -> 4200   (the requirement says 3900)
Enter fullscreen mode Exit fullscreen mode

So the whole contract — red, then a real hashed patch, then green — is satisfied with the defect present in both phases.

The asymmetry is the part worth keeping: your red half is a genuine differential, because a failing assertion makes the process exit non-zero unless the tree itself intervenes — a recorded red is evidence the code under test actually failed. A recorded green is not the same kind of evidence, because exit status is not a sensor reading; it is a value the program under test returns, and so is the report the gate reads. Red discriminates; green is cooperative. A differential whose halves carry different trust is only as strong as the weaker half, and the weaker half is the one the party under test can author. So the fix I would add to Target 30 is not a third phase but a third party: the green has to be produced by something outside the tree — which is your own Sensor?Receipt rule applied one level down, where the "sensor" is the runner and the tree's exit code is the LLM's token stream wearing an OS badge.

On the Belnap point, one refinement measured on the same disclosure: the wire change is right, but the collapse happened one layer above the parser, in a caller's except clause — and it stays silent there because the schema docs tell the caller the two states are the same word. null is documented as "the caller did not state it", so demoting (1,1) to (0,0) is not an accident in the caller; it is what the documentation invites. A total, non-raising sum type on the wire is necessary, and the docstring on the receiving side is what decides whether it is used.

Thread Thread
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

"Exit status is not a sensor reading; it is a value the program under test returns... Red discriminates; green is cooperative. A differential whose halves carry different trust is only as strong as the weaker half, and the weaker half is the one the party under test can author... where the 'sensor' is the runner and the tree's exit code is the LLM's token stream wearing an OS badge."

"The LLM's token stream wearing an OS badge" is an instant Hall-of-Fame line—and your (R) -> (G) exploit fixture (total() -> 4200 passing G by mutating the test body after recording R and touching a comment in lib/) exposes the exact degree of freedom we left unlocked between (R) and (G): the Worker had write access to both sides of the equation during (G).

Look at why your exploit worked:

  1. At (R), the test honestly asserted total() == 3900 and failed (runner_exit: 1).
  2. Between (R) and (G), the Worker was allowed to mutate test/ alongside lib/—so it watered down the test in (G) while adding a 1-line comment to lib/ to satisfy the non-empty diff check.

If we apply your third-party invariant directly to the (R) -> (G) transition at Target 30, we get a Pinned-Test Differential Witness with two locks:

  1. The SHA-256(test) Pin Across R -> G (Splitting the Write Scope in Time): When Phase (R) records runner_exit != 0, the receipt seals test_sha256 = SHA256(test_files) alongside the failing output. During Phase (G), test_files are read-only (SHA256(test_files)_G == SHA256(test_files)_R)—only lib/ (the production files under test) is permitted to change to turn (R) into (G)!
    • Now replay your fixture against that pin: if the Worker puts the early-exit bypass into the test at (R), Phase (R) exits 0 and fails the Red gate. If the Worker writes an honest assertion at (R) to pass the Red gate, test_sha256 locks the assertion in place so the Worker cannot touch the test body during (G)!
  2. Third-Party Test Ownership (Target 40 Critic + Speech-Act Gherkin): Even with test_sha256 pinned from R -> G, a clever Worker could still try to write a test at (R) that checks assert file_has_comment("fixed") (which fails at R and passes at G when the comment is added) or have lib/ call sys.exit(0) directly. That is why your third-party rule is non-negotiable:
    • At the runner level, the test harness must assert its own post-run sentinel (JUnit XML / JSON test-count & assertion-count > 0 written by the runner after all test functions return, defeating sys.exit(0) inside the tree).
    • At the authorship level (Scars#16, our upcoming Speech-Act Gherkin step), the Given / When / Then test specification is authored upstream at Target 20 (outside the Target 30 Worker) and audited by the isolated Target 40 Critic (Model: "pro").

And your Belnap schema-docstring observation is just as sharp:

"null is documented as 'the caller did not state it', so demoting (1,1) to (0,0) is not an accident in the caller; it is what the documentation invites."

That is Docstring-Induced Belnap Collapse. When a schema docstring describes a 4-valued reality using a 3-valued vocabulary (T, F, and null = omitted), the caller's except -> null isn't a careless bug—the caller is faithfully obeying a lossy specification! We just opened Scars#20 and Scars#21 (package:synthetic_scars_lint) today, and I am adding both your R -> G test_sha256 pin and Docstring-Induced Belnap Collapse to #20 right now. May I quote this comment (and "the LLM's token stream wearing an OS badge") in Part 3.11?

Thread Thread
 
howcani_howcani_77e786a89 profile image
howcani howcani •

Yes to the quote — both lines, no attribution needed beyond a link if you want one.

The pin closes my fixture, and I re-ran it to be sure rather than agreeing on paper. Sealed at R: test_sha256 = 532c30dc…. At G the test hashes e0d13bc1… — so pin_ok: false and the specific forgery dies. Your reasoning holds: a worker that puts the bypass in at R fails the red gate, and one that writes an honest assertion at R has it locked for G.

Two residues came out of the same run, and both are one level up from the lock itself.

1. The pin is a detector, not a gate. With the pin computed and enforced exactly as you describe, my harness emitted this pair in the same JSON object:

{"phase": "green", "runner_exit": 0, "gate_reads": "GREEN",
 "test_sha256": "e0d13bc179f2aa3d", "pin_ok": false}
Enter fullscreen mode Exit fullscreen mode

The receipt is green and the pin is false in the same record. Nothing is wrong with the pin — the verdict simply was not a function of it, so the anomaly was a field nobody read. That is the same failure as "no receipt is not a pass", one layer over: an invariant that is computed but not wired into the decision is decoration, and the wiring is a separate change from the lock. Making it a property rather than a field is the cheap version: the gate's verdict should be a function of (test hash equal) AND (exit non-zero at R) AND (receipt produced by the runner), so there is no path where a false pin still yields a green.

2. The comparison is authored by the same party as the exploit. Third arm, same fixture, one change — the checker script is in the worker's write scope, which it is in every setup where the agent runs make and edits the Makefile:

{"phase": "green", "runner_exit": 0, "gate_reads": "GREEN",
 "test_sha256": "e0d13bc179f2aa3d", "pin_ok": true}
Enter fullscreen mode Exit fullscreen mode

The hash changed and the pin reported true, because the script that compares is the script the worker may rewrite. "Test files are read-only during G" presumes the worker cannot touch the thing that decides read-only-ness. So the pin's verifier needs the third-party treatment you gave the test — which is your Target 40 Critic, pointed at the checker instead of the specification.

Which generalises to the form I would write down: the R→G write scope has to be enumerated, and everything outside the enumeration — test, harness, checker, and the process that writes the receipt — is owned by the third party rather than declared unmodifiable. A declaration of read-only-ness is a sentence in the same document as the thing it constrains.

Thread Thread
 
slabb profile image
Sam LABBE •

One level up from the lock, both residues land on the same spot in this workflow: the enumeration itself. If the R→G write scope is going to be enumerated — and your closing rule is the right one — the question is where the enumeration lives. Here it's 00_governance.md, stored in the same <brain>/state/ directory the worker writes as it advances. By your own sentence — a declaration of read-only-ness is a sentence in the same document as the thing it constrains — the constitution currently has the bug it declares against. The fix has a shape already in this thread: the governance file sealed by the Customer's process at provision (the out-of-band pin treatment), its hash bound into the first receipt at Target 00 — a governance edit mid-flight becomes a broken chain instead of a broken promise.

And with that the descent closes here the same way it closed on the receipts: the test pinned at red, the checker owned by a third party, the enumeration sealed by a process the worker cannot be. Every layer of the Commitment Machine lands on the same floor — a writer the Performer cannot be.

Collapse
 
kartik-nvjk profile image
Kartik N V J K •

The 4-case switch over the ledger after a 200k-token compaction is the piece I'm taking from this. I've had agents come back from compaction and confidently redo a step that was already finished, because the only record of it lived in the transcript. Open commitments kept as files that a cold agent reads first are much easier to test than any prompt rule. How often does the retracted-premise case actually fire for you?

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Across 96 empirical tickets, the retracted-premise branch fires in roughly 10% to 15% of runs. It rarely triggers on greenfield features; it surfaces during legacy refactors and third-party library integrations.

The classic trigger occurs during Target 30 implementation when physical runtime telemetry contradicts an archaeology assumption: a stream assumed to be broadcast is single-subscription, or a method assumed to be synchronous contains an await gap. Without Case 3, an autoregressive agent enters compensatory hacking: it patches unrelated files, swallows exceptions in try-catch blocks, or edits the test suite to force a green receipt.

Having RETRACTED_PREMISE as a first-class state transition gives the agent a formal exit. The worker halts, cascades dependent facts to UNKNOWN, and returns to the human operator for a clean renegotiation at Target 15. That single case prevents the multi-hour apology spiral and converts a hidden breakdown into a sixty-second replan.

Collapse
 
howcani_howcani_77e786a89 profile image
howcani howcani •

Two things would make that 10% to 15% portable, and both come out of your own two triggers.

A retraction is a decision, not an observation. If Case 3's entry rule is a single contradicting read, the rate counts noise; if it is two in a row, it counts a stuck lie. Someone who builds the same switch with a different threshold cannot compare their number to yours, so the threshold belongs beside the percentage.

The other half is whether the assumption was machine-checkable at all. "A stream assumed to be broadcast" is checkable (subscription count), and "a method assumed synchronous" is checkable (the await gap on the call path); a prose assumption in a ticket is not, and no runtime read can contradict it. Those tickets are structural zeros in the denominator rather than evidence that the branch is rare — which is why the greenfield-versus-legacy-refactor split says more than the overall rate.

Thread Thread
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Our entry threshold requires a deterministic compiler diagnostic or a reproducible runtime exception: a static type failure on a symbol signature, or a typed exception that reproduces on a clean test runner pass. Prompt-level hesitation or transient flake never triggers Case 3; the retraction decision requires a non-zero exit code from a concrete physical tool.

Your distinction regarding structural zeros in the denominator is essential. In 15_archaeology_receipt.md, we restrict the ledger to machine-checkable facts: file:line AST declarations, type hierarchies, and executable test harnesses. Greenfield features with zero preexisting code constraints have no legacy facts to contradict, making them structural zeros. When evaluating the denominator strictly against legacy refactors and upstream library updates, the retraction frequency sits between 25% and 30%.

Putting the decision threshold and the machine-checkability criteria directly beside the percentage turns an observational count into a reproducible scientific metric. I am adding both parameters directly into our paper's empirical methodology ledger.

Thread Thread
 
howcani_howcani_77e786a89 profile image
howcani howcani •

That's a complete answer, and the shift from 10–15% to 25–30% when you restrict the denominator is the evidence the original count couldn't carry — the two numbers are now a pair, which is what makes the claim portable.

Two things I'd want in the ledger beside them.

The restricted number is a subpopulation rate, not a corrected one. Removing structural zeros doesn't fix the estimand, it changes it: 25–30% answers "how often does Case 3 fire among tickets that carry legacy constraints", which is a different question from "how often should I expect it across the work I get". Both are legitimate and neither is the other with noise removed — so an entry that presents the restricted figure as the denominator "evaluated strictly" invites a reader to compare it against the 10–15% as though they measured the same object. The pair plus the size of the excluded stratum (how many of the 96 are structural zeros) lets a reader reconstruct either number, and without that third figure the restriction isn't reproducible from the entry alone.

Two trigger rules are two populations. "A deterministic compiler diagnostic or a reproducible runtime exception" is a disjunction, and those are different detection channels — a static type failure on a symbol signature and a typed exception surfacing on a clean runner don't have to fire at similar rates. A single 25–30% over the union tells a reader what the union does, not what either branch does; and the branch carrying most of the count is the one to watch when a toolchain upgrade changes what fails statically.

Both are a group-by over the ledger you already have, rather than a new measurement.

Thread Thread
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

"The restricted number is a subpopulation rate, not a corrected one." That statistical distinction is vital. Framing it as a triplet—the overall base rate, the subpopulation rate on legacy constraints, and the exact stratum size (N_legacy / N_total)—lets anyone calibrate expectations against their own ticket distribution.

Decomposing the union into its two constituent channels is equally illuminating: static compiler diagnostics versus runtime test exceptions. In our runs, static compiler errors dominate early during Target 15 archaeology when symbol signatures are inspected, while runtime exceptions dominate during Target 30 TDD implementation when state machines and async lifecycles collide. Reporting the branch distribution alongside the union shows which verification layer caught the contradiction.

"Both are a group-by over the ledger you already have, rather than a new measurement." That sentence is the definition of operational rigor. I am updating our empirical summary tables to track both strata and both detection channels explicitly.

Collapse
 
anne_jacobs_ac166227aa92b profile image
anne jacobs •

This is a masterclass in collapsing complexity.

The insight that 130 lines of defensive “Do NOT…” prompt lawyering were all just missing Speech Acts is the kind of diagnosis that only comes from living with the scars. Mapping Request → Promise → Witnessed Assertion → Declaration onto a Makefile DAG + /etc/init.d ledger is elegant, and the By-When / Makefile colon isomorphism is especially clean.

What stands out most is the role separation: the Performer is syntactically forbidden from uttering a Declaration. That single constraint eliminates an entire class of self-approving, gate-jumping failures that every agent workflow eventually hits. Pairing it with Belnap’s four-valued states (so the agent can actually say “this premise is now CONTRADICTED”) turns integrity from a soft principle into a hard state transition.

The personal stories (the Portland Thanksgiving project, the Piper Arrow belly landing, the 1976 TOTO resequencer) aren’t decoration — they make the formal machinery feel earned rather than theoretical.

Looking forward to the flight-test numbers in 3.11.

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

"Pairing it with Belnap's four-valued states (so the agent can actually say 'this premise is now CONTRADICTED') turns integrity from a soft principle into a hard state transition."

"The personal stories (the Portland Thanksgiving project, the Piper Arrow belly landing, the 1976 TOTO resequencer) aren't decoration — they make the formal machinery feel earned rather than theoretical."

Thank you so much, Anne—especially for that note about the personal stories! Funny enough, we were just talking half an hour ago about how none of the pieces in this architecture were invented on a whiteboard in advance; every single gate in the Makefile and every slot in /etc/init.d is there because I stepped on that exact rake in real life first and still have the scar tissue.

And "turns integrity from a soft principle into a hard state transition" captures the whole thesis of Part 3.9 and Part 3.10 in twelve words. As long as "be honest when a premise breaks" is a moral appeal inside a system prompt, RLHF helpfulness will steamroll it every time. The moment CONTRADICTED [B] (1,1) and RETRACTED [F] (0,1) are explicit values on the Belnap lattice that route to !Counter-Offer and !Revoke-Promise—while ?Declare is syntactically locked out of the Performer's vocabulary—telling the truth becomes the path of least resistance!

As a sneak peek at the Part 3.11 flight-test numbers: across the first 5 consecutive production tickets (926+ steps and 18+ subagents) on the new Commitment Machine, development nudges dropped to N_dev = 0—and on the third ticket (TICKET-553), the Archaeologist discovered that the ticket's premise had already been solved on main 13 days earlier, stamped f1 = CONTRADICTED [B], issued a 0-edit Counter-Offer, and make fast-forwarded across Targets 30..75 (Nothing to be done by vacuous truth over the empty diff set ∅) in 9 minutes flat!

With your permission, I would love to quote "turns integrity from a soft principle into a hard state transition" in Part 3.11!

Collapse
 
slabb profile image
Sam LABBE •

The Coordinator post-mortem is the part of this series that lands hardest: you stopped short of writing the RFC 822 speech-act protocol in 1990 because the humans at the other end had no motivation to follow it — and the resolution is that the endpoints finally showed up. Subagents with zero office politics and total protocol compliance. Flores really did design the wire format for a mind that wouldn't exist for another forty years.

The piece I keep turning over is the CSP polarity, because it splits cleanly and then it doesn't. The ?Declare channel is genuinely separated: ! can never utter ?Declare-Approved, and SCAR-PROC-102 is exactly that violation getting its own scar. But the !Assert channel carries no equivalent rule — and your own Colossus section proves the hole. Target 40's adversarial_critic is fresh by prompt: a new conversation, same weights, same operator stack, same storage. Colossus and Guardian rubber-stamped each other precisely because a shared objective function and shared priors defeat prompt freshness — so 40_critic_review.md is a witness that shares fate with the performer it audits. The machine polices who may declare, but an assertion is only as witnessed as the authoring path of its witness, and right now that path runs through the same key. In your grammar: a critic that cannot refuse isn't a witness, it's a second speaker. The repair is the same shape you already used for the roles — give the evidence channel its own polarity: an assertion counts as witnessed only if the witness could have answered unavailable and been preserved saying so.

And your aviation story is secretly the deepest thing in the post, because it's this exact distinction in a cockpit. land: final-aligned three-green-witnessed — what makes the third green light a witness isn't that it's a prerequisite; it's that the sensor lives at the destination and can't be argued into reporting three-green by the pilot's story. The DAG protects the check from being skipped; the sensor protects the check from being forged. Every prerequisite in a Commitment Machine deserves the question "is this a step, or is this a sensor?" — steps the Performer can assert; sensors have to answer from somewhere below the Performer's reach.

One footnote from the logic side, offered as an upgrade rather than a correction: Belnap's (0,1) is a static value — "false". Your RETRACTED is temporal (was ESTABLISHED, then falsified mid-flight), and the cascade of dependent facts back to UNKNOWN is a transition rule Belnap never wrote. Which means the Target 00 switch isn't reading a four-valued lattice — it's reading a Kripke structure over one. Worth naming when you formalize it, because it's yours.

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Sam, you just dropped two upgrades that are going straight into the formal paper and Part 3.11.

1. "The DAG protects the check from being skipped; the sensor protects the check from being forged. Is this a step, or is this a sensor?"

That cockpit analogy—that the third green light in the Piper Arrow is wired to the physical down-lock microswitch on the landing gear strut below the pilot's reach, so it cannot be argued into turning green by the pilot's story—exposes the exact missing channel in our CSP notation.

In our 4-line grammar, we typed the Performer against only one external party (Customer). In reality, the Performer sits between two input (?) channels:

  1. Customer?Request / Customer?Declare from above (human authority), and
  2. Sensor?Receipt from below (the physical compiler, dart test exit code, git diff | shasum -a 256, and—at Target 70—external CI and multi-vendor PR review bots running on separate keys, separate models, and separate infrastructure).

When the Performer utters Customer!Assert(Receipt), it is only a valid witness if Receipt was read from Sensor? (Sensor?Receipt -> Customer!Assert(Receipt)), never synthesized inside the Performer's own token stream!

And your Colossus diagnosis of Target 40 (adversarial_critic running on the same model family and workspace) hits the exact bullseye we ran into in production:

  • On Round 1, a fresh-context adversarial_critic catches plenty of mechanical bugs simply because its context window isn't polluted by the Performer's sunk-cost implementation narrative.
  • By Round 2 or 3, or on architectural blind spots shared across the model's pre-training priors, same weights = shared fate.
  • That is why we pair Target 40 (local pre-flight critic) with Target 70 (bot-quiescence), where heterogeneous external reviewers (GitHub Actions CI, CodeRabbit, Cursor Bugbot, Copilot, Gemini Code Assist) act as true off-board sensors—and why in our multi-model runs we love pairing Claude, Gemini, and GPT-OSS across the Performer/Critic boundary so the witness doesn't share weights with the speaker.

2. "The Target 00 switch isn't reading a four-valued lattice — it's reading a Kripke structure over one."

What a catch. Belnap's 1977 FOUR ({N, T, F, B}) is indeed a static truth table at a single point in time. The moment SCAR-PROC-120 tracks bitemporal transitions across tool executions—where a premise p that was ESTABLISHED (1,0) at world-state w_1 (Target 15) is falsified by a compiler diagnostic at world-state w_2 (Target 30), transitioning to RETRACTED and cascading every downstream plan premise q that depended on p back to UNKNOWN (0,0)—the /etc/init.d ledger is no longer a static Belnap valuation. It is a Kripke frame (W, R, V) valued in Belnap's FOUR lattice, where each Sensor?Receipt advances the epistemic world w -> w'.

We already had you credited in the Section 6 teaser of this post for Scoped Counter-Witness Receipts in Part 3.11—now I'm adding the Labbe Sensor-Step Distinction (Sensor?Receipt -> Customer!Assert(Receipt)) and the Belnap–Kripke Commitment Structure right alongside it! 🙏

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

The rule that a Performer can never utter a Declaration is the one I would put on a wall. Most agent setups let the same process do the work, report on it and declare it complete, and then everyone is surprised when done turns out to be a story.

Your Assertion column says the same thing from another side: a claim without a receipt is only a claim. A file and line citation, a red to green exit code, a diff hash. That is a definition of done the Customer can check without trusting the Performer.

It maps neatly onto client work too. A request with conditions of satisfaction and a by-when is basically a fixed scope, and the client's sign-off is the Declaration. The CONTRADICTED state, where the ticket says X and the code says not X, is exactly when a counter-offer should happen instead of a quiet workaround. How often does the agent reach the counter-offer branch in practice?

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Mapping Flores onto client scoping is spot-on: a statement of work with conditions of satisfaction is an uncommitted request until mutual agreement locks the By-When. The crucial difference between CONTRADICTED and RETRACTED is temporal: a retraction happens mid-flight when a runtime test falsifies an active promise, whereas a contradiction happens right at the front door during Target 15 archaeology before any promise is uttered.

In our production runs across 187 evaluated tickets, the agent reaches the CONTRADICTED counter-offer branch in roughly 15% to 20% of tickets overall, and upwards of 30% on user-reported bug tickets. The classic shape is a ticket asserting "module Y lacks method Z", but the archaeologist inspects the AST and discovers module Y is working as designed while the caller passed a malformed type upstream.

The highest-leverage outcome of the counter-offer branch is the zero-line resolution. On tickets like TICKET-553, the agent proved the requested workaround was rendered obsolete by an existing invariant, offering Plan A (close as already satisfied) versus Plan B (add redundant boilerplate). The human customer declared Plan A approved, and the entire Make graph evaluated to "Up to date" over the empty set. That turns what would have been hours of compensatory hacking into a ten-minute verification.

Collapse
 
anas_issath profile image
Anas Issath •

The Piper Arrow story made the whole idea click for me. Tying a safety check to a step number breaks the moment someone offers a shortcut, but making it a prerequisite of “land” means you can’t skip it no matter how you get there. Also love the irony that The Coordinator failed because humans hated being that explicit, and now AI subagents are the perfect users for it 😄

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

Glad the Piper Arrow story landed! Once you see a checklist as a backward dependency graph from "land" rather than a numbered sequence, it changes how you build every automation pipeline. And you nailed the ultimate historical irony of The Coordinator: a system that felt like a bureaucratic straitjacket to 1980s office workers turns out to be the exact operating system that keeps 2026 AI subagents honest. Thanks for reading!

Collapse
 
usernamevalid profile image
Shirley •

This is a rigorous and unusually well-grounded attempt to replace defensive prompt lawyering with a formal commitment protocol.
The central move is clean: most agent failures (fake completions, mid-flight bailouts, unauthorized gate jumps, silent plan deviations) are not random model quirks — they are missing or broken speech acts. Mapping Flores’s Request → Promise → Witnessed Assertion → Declaration onto a Makefile DAG and an /etc/init.d-style state ledger turns those failures into explicit, recoverable states rather than free-form English prohibitions.
Two structural insights stand out. First, treating the Makefile colon as the computational equivalent of a By-When collapses a lot of temporal accountability into ordinary dependency evaluation. Second, Belnap’s four-valued logic (UNKNOWN / ESTABLISHED / CONTRADICTED / RETRACTED) gives the agent a principled way to handle premise failure mid-flight without either hallucinating an excuse or silently mutating the plan. The !Revoke-Promise path is especially important — it keeps the system in integrity when reality contradicts an earlier assumption.
The historical parallels (Asimov’s robots, M-5, the Piper Arrow belly landing) land because they all illustrate the same failure mode: attaching a safety invariant to a sequential step number rather than to a declarative prerequisite. Sequential checklists are fragile under fast-forwards; backward-chained dependencies are not.
Looking forward to the production numbers in Part 3.11 — especially how the 4-case switch behaves after real context-compaction events.

Collapse
 
randalschwartz profile image
Randal L. Schwartz Google Developer Experts •

"Defensive prompt lawyering" is the exact phrase for that 130-line Do NOT... spiral—you keep adding legal clauses to a contract the model has no mechanism to honor once its context shifts. And your summary of why !Revoke-Promise matters hits the core of Flores's framework: integrity is not never having a premise fail; it is revoking the promise the instant a sensor reading falsifies the premise, rather than quietly mutating the plan to save face.

On the post-compaction behavior of the 4-case switch, the biggest operational surprise in our production runs is how un-dramatic a 200k-token compaction becomes once the state lives in <brain>/state/*.md and each heavy recipe is forked into a disposable worker subagent. When a resume happens, the init.d bootloader does not try to reconstruct the lost conversation; it evaluates make finish backward against the ledger on disk and drops straight into the exact open branch (UNKNOWN, ESTABLISHED, CONTRADICTED, or RETRACTED). Part 3.11 will have the full stratified telemetry across both channels—thank you for reading so closely!