Why did six modern AI prompt grammars—Graphviz digraphs, SysV /etc/init.d cards, Gherkin scenarios, EBNF speech-act rules, Belnap epistemic tables, and rewind checklists—all collapse into a single 1980 Smalltalk-80 keyword message cascade? Because Alan Kay and Dan Ingalls solved the static serialization of algorithms and data structures on the back of a postcard forty years before anyone built a Transformer.
"It's fascinating that for decades, we tried to work out 'similar text' through complex algorithms, and now that we have cosine-similarity, nobody recognizes fully how to use it. If anything defines the past few years, it's 'cheap cosine similarity', now in hardware." — Randal L. Schwartz
"I've learned more than 90% of the devs about how LLMs work. I may even know more than most of the 'experts' at the labs, since I'm a field engineer... Without control surfaces, a bigger engine just means the plane still noses into the river—just more accurately, or at a greater carbon footprint." — Randal L. Schwartz
"Wait a minute. In 1980, Alan Kay and Dan Ingalls designed a syntax that fits on the back of a postcard, has only three message precedences (unary, binary, keyword), has zero grammar ambiguity, serializes live call-stack frames (
thisContext) to disk via#storeString, logs append-only atomic deltas via.changes, and compacts memory viaSmalltalk condenseChanges... What happens if we replace all six notations with 1980 Xerox PARC Smalltalk-80?" — Randal L. Schwartz"The stack is 40 years old and none of it was invented for LLMs." — Francisco Grandón (
@fgrandon)
⚡ TL;DR (For the Impatient Systems Architect)
- The Great Historical Inversion: From 1975 to 2018, computer scientists tried to fake fuzzy semantic similarity using complex deterministic algorithms (Soundex, Levenshtein, TF-IDF) and hit a wall—while deterministic state machines (
make, Smalltalk VMs, compiler ASTs) were already 100% solved. Now that GPUs and TPUs finally gave us cheap cosine similarity in hardware, the software industry threw away the deterministic state machines and tried to use fuzzy cosine similarity to fakeforloops, call stacks, and dependency graphs. - Why Six Notations Collapsed Into One 1980 Postcard: Across Parts 3.6 through 4.1, our agent control plane accumulated six different notations (
Makefile, SysV/etc/init.dMarkdown cards, Graphvizstrict digraph, CucumberGherkin, RFC 5234EBNFspeech acts, and Belnap 4-valued tables). Maintaining six parsers created boundary friction. Why did 1980 Xerox PARC Smalltalk-80 eat the inner five while pairing with1976 Makeon the outside? Because Niklaus Wirth'sAlgorithms + Data Structures = Programsrequires a static syntax that represents both data and deferred execution without ambiguity. Unlike 1960 Lisp (which overloaded(...)and needed special forms and macros, sufferingDyck-1closing-parenthesis drift))))on smaller models) and 1988 Tcl / 1990expect(where{...}is an unparsed string vulnerable touplevelscope loss), Smalltalk separates eager grouping(...)from 2-byte lexicalBlockClosures[:x | ...]. Every control structure (ifTrue:ifFalse:,whileTrue:,on:do:) is an ordinary keyword message send, allowing#storeStringand.changeschunks to serialize live call stacks (thisContext) and execution state machines in one grammar that fits on a postcard. - The
Suite 83-Arm Controlled Shootout (N = 360Trials Inside Our2,040-Trial Benchmark): Under~16,000tokens of realistic compiler and diff distractor load across four model tiers (gemini-3.5-flash-lite,gemini-3.8-flash,gemini-3.1-pro-preview, andgemini-4-argon), Arm A (English Prose + Gherkin + init.d) scored2.5%(3/120), Arm B (Common Lisp S-Expressions) scored91.7%(110/120, dropping to66.7%onflash-litedue to optional&keyomission and))))parenthesis drift), and Arm C (Smalltalk-80 Keyword Cascades) hit100.0%(120/120,z = 15.11,p < 0.0001vs. Prose;z = 3.23,p = 0.0012vs. Lisp). In Smalltalk, omitting the sad-path branchonFailure:changes the selector from#state:invariant:onSuccess:onFailure:to#state:invariant:onSuccess:—an immediate compile-timeMessageNotUnderstooderror. - Seven Live Production Flight Tests (
FT-236ThroughFT-242— Over6,400Combined Steps Across Two Cohorts): We flight-tested the Smalltalk-80thisContextImage Bootloader (baseline.sources+changes-*.changes+Smalltalk condenseChanges) across seven consecutive production tickets (4in open-sourceBlocSignaland3in a commercial enterprise medical-imaging monorepo), bringing our cumulative ledger to250evaluated tickets (144Cohort A +106Cohort B),0.0%repeat regression rate (0/250),1.88mean Socratic nudges, and607codified scars (syntheticscars.org). - The Four Irreducible Attractors of Agentic Coding: Strip away every prompt fad of 2023–2026 and four orthogonal, 40-year-old foundations remain: (1) 1976 Stuart Feldman
Make, (2) 1980 Alan Kay & Dan IngallsSmalltalk-80, (3) 1994/1999 Kent BeckSUnit/TDD, and (4) Clean-Room Six-PillarAdversarial Review+Somatic Scars.
1. The Great Historical Inversion: Cheap Cosine Similarity in Hardware
In Part 4.1, we ended with a teaser: what happened when we replaced six separate agent control-plane notations with 1980 Xerox PARC Smalltalk-80?
To understand why a 46-year-old programming language from Palo Alto is the missing execution fabric for 2026 Large Language Models, step back and look at the forty-year arc of computer science.
From 1975 to 2018, programmers knew how to build deterministic state machines in our sleep. Stuart Feldman gave us make in 1976; Dan Ingalls and Alan Kay gave us the Smalltalk-80 virtual machine in 1980; compiler writers gave us LALR parsers, static type checkers, and SSA control-flow graphs. None of those ever "forgot" what step of a loop they were on or hallucinated a method name.
What we could not do from 1975 to 2018 was fuzzy semantic similarity. For four decades, we threw every deterministic algorithm we could think of at the problem of asking "Does text A mean roughly the same thing as text B?"—Soundex, Levenshtein edit distance, Porter stemmers, n-gram overlaps, TF-IDF, BM25, and brittle regular-expression heuristics—and we hit an absolute brick wall.
Then, over the past few years, the semiconductor and machine-learning industries handed us a genuine miracle: cheap cosine similarity in hardware. With modern GPUs and TPUs computing scaled dot-product attention (softmax(QK^T / sqrt(d_k))V) across high-dimensional embedding spaces at trillions of operations per second, the fuzzy semantic matching problem that stumped us for forty years was suddenly solved in silicon.
And in response to that miracle, the software industry performed one of the most baffling backflips in engineering history:
We threw away our deterministic state machines, and tried to use fuzzy cosine similarity to fake for loops, call stacks, and build pipelines.
From 1994 Perl Markov One-Liners to 1999 spew to 2026 Transformers
In Part 3.9, I showed a weighted-BNF un-parser called spew that I wrote in the late 1990s (featured in my September 1999 Linux Magazine column, A Little Spew Is Good For You, and Revisited in April 2004). Even earlier, back in 1994 on comp.lang.perl, we used to amuse ourselves writing character-level and word-level Markov text generators in two lines of Perl:
# 1994 Usenet Perl Markov Chain: push @{$follow{$w1, $w2}}, $w3; $next = $follow{$w1, $w2}[rand @{$follow{$w1, $w2}}]; When I evolved that Markov hash into spew in 1999, I added weighted production rules (@) so you could bias the random walk toward realistic grammatical structures:
# 1999 spew weighted-BNF grammar: commit_action: 5 @ run_unit_tests_then_commit | 2 @ git_commit_no_verify_shortcut If you have ever written a Markov generator or a weighted grammar like spew, you have zero mystique about what a modern Large Language Model is doing at inference time:
A 2026 Transformer is
spewwhere the@branch weights are dynamically computed on the fly by hardware cosine similarity (QK^T) across a 128,000-token context window instead of static hash keys.
That is a superpower when you need to synthesize a Cupertino widget builder, translate a complex binary imaging specification into Dart, or spot a subtle architectural smell in a diff.
But nobody in 1999 would have deleted /usr/bin/make, thrown away their compiler's symbol table, and put spew in charge of remembering which 14 files had already been compiled or whether the production database migration passed! Yet every time a developer writes a 2,000-line Markdown prompt (Step 1: Do X. Step 2: Loop over Y. Step 3: Don't forget Z...) and hopes the LLM stays on the rails for two hours, they are asking spew to be an operating system.
A Synthetic Scar paired with a Smalltalk StateGraph is simply setting 0 @ git_commit_no_verify_shortcut (-∞) in the grammar and evaluating a deterministic invariant: [ :ctx | ... ] block before spew is allowed to roll the dice.
Langley's 52-Horsepower Potomac Crash vs. The Wright Brothers' 12-Horsepower Control Surfaces
Why have so many frontier AI labs missed this, while a field engineer running production tickets all summer tripped right over it?
Think back to December 1903. Samuel Pierpont Langley, the Secretary of the Smithsonian Institution, had 70,000 USD in War Department and Smithsonian funding, the prestige of the scientific establishment, and Charles Manly's radial engine—an engineering marvel that produced 52 horsepower. On December 8, 1903, Langley's Great Aerodrome was launched from a houseboat catapult on the Potomac River, reared straight up, snapped its rear wing, and plunged into the icy water like a stone.
Nine days later, on December 17, 1903, two bicycle-shop field engineers from Dayton, Ohio—Orville and Wilbur Wright—flew into history at Kitty Hawk using a crude home-built engine that barely wheezed out 12 horsepower.
Why did the 12-horsepower bicycle mechanics fly while the 52-horsepower Smithsonian physicist nose-dived into the Potomac?
Because Langley believed flight was an engine-scaling problem—strap enough horsepower to an airfoil and brute force will keep it aloft. The Wright brothers realized from thousands of glider flights in the wind at Kill Devil Hills that flight is a three-axis control-surface problem. Before they ever bolted an engine to the flyer, they solved roll (wing-warping), pitch (the forward elevator), and yaw (the rear rudder).
Today's frontier AI discourse is packed with modern Langleys—including the "Next-Model" cargo cultists who spend all week datamining IDE binaries for leaked model codenames ("Sure, my one-shot vibe prompt crashed at 120k tokens today, but wait until next Tuesday's 500-horsepower model drops!").
Without three-axis control surfaces—topological dependency ordering (Make), a persistent reified execution frame (Smalltalk-80 thisContext), deterministic regression witnesses (TDD), and clean-room adversarial verification (Six-Pillar Critic + Somatic Scars)—a bigger cosine-similarity engine just means the plane still noses into the river, just more accurately, or at a greater carbon footprint (both in datacenter megawatts and in the biological carbon of the human engineer woken up at 3 AM!).
2. Why Six Grammars Collapsed Into One 1980 Postcard (Wirth's Equation & The 3-Way Language Fork)
At the end of October, Francisco Grandón (@fgrandon) published a great field report on DEV.to: What Broke After I Moved My Agent's Rules Into a CLI — And How Synthetic Scars Reshaped agops v2.1.
Francisco had adopted our Target 15 / Target 20 Graphviz strict digraph state-machine verification (SCAR-PROC-152, where every action node must have out-degree >= 2 with explicit ok and fail edges) inside FranciscoGrandon/ag-ops. And almost immediately, his CLI hit what he documented as Scar #2: when an LLM wrote valid Graphviz DOT statements separated by newlines instead of semicolons, his attribute-stripping regex (/\s*\[[^\]]*\]/g) ate the newline before [label="ok"], concatenating two edges into A -> BB -> C and silently corrupting the graph!
When I read Francisco's post-mortem, I looked at our own control plane in WORKFLOW.mk and realized something uncomfortable. Over the course of Parts 3.6 through 4.1, each time we solved a failure mode by riding pre-training gravity (Part 3.8), we had recruited a classic systems notation for that layer:
- 1976 GNU
Makefile(target: prerequisites) for backward-chained dependency topology (Part 3.6); - 1983 SysV
/etc/init.dNumbered Cards (00_...mdthrough99_...md) for context-compaction survival (Part 3.7); - 1986 Winograd & Flores Speech-Act Tables + RFC 5234
EBNF+ HoareCSP(!/?) for role boundaries (Part 3.10); - 1977 Belnap 4-Valued Epistemic Tables (
{T, F, Both, Neither}) for premise verification; - 1991 Graphviz
strict digraphfor Mealy state-machine branch auditing (out-degree >= 2); and - 1998 Cucumber
Gherkin(Given / When / Then) for mapping state paths onto unit tests.
Every single one of those six notations worked vastly better than unstructured English prose. Yet maintaining six separate miniature parsers, regexes, and translation steps across boundaries was still accidental complexity. A Graphviz .dot node couldn't directly invoke a Belnap retraction; a Gherkin Then clause couldn't directly unwind an /etc/init.d card; and a regex stripping .dot brackets could eat a newline.
Why had we needed five different notations inside our Makefile targets in the first place?
Solving Niklaus Wirth's Equation (Algorithms + Data Structures = Programs) in Static Text
In 1976, Niklaus Wirth titled his classic textbook Algorithms + Data Structures = Programs.
When you checkpoint an AI agent's brain to disk so it can survive a 200,000-token context compaction—or hand a formal plan from the Planner to the TDD Worker and the Clean-Room Critic—you need a static text representation on disk that captures both halves of Wirth's equation at the exact same time:
- Pure Data Formats (
JSON,YAML,TOML, Graphviz.dot, SysV Markdown tables) represent Data Structures, but they are 100% blind to Algorithms. BecauseJSONand.dothave no native representation for deferred execution (if,while,try/catch, lexical closures), you are forced to stuff executable invariants inside dead string literals (label="ok: failing_tests > 0"), where no parser can type-check or execute them without inventing an ad-hoc mini-language inside the strings! - Mainstream Imperative Languages (
C,Python,Dart,TypeScript,Rust) represent Algorithms, but they cannot natively serialize their own live Data Structures + Call Stack (thisContext) back out as valid source code (#storeString), because their control-flow constructs (if (...) {},for (...) {},try {} catch {}) are hardwired parser statements, not first-class objects or messages.
In the entire history of programming languages, only three language families seriously attempted to unify Algorithms + Data Structures into a minimal, uniform, homoiconic syntax where code and data share the same structural representation: 1960 Lisp, 1988 Tcl (and 1990 expect), and 1980 Smalltalk-80.
Let's put all three side by side under Transformer attention physics:
| Dimension | 1. Lisp (McCarthy, 1960) | 2. Tcl & expect (Ousterhout 1988 / Libes 1990) | 3. Smalltalk-80 (Kay & Ingalls, Xerox PARC 1980) |
|---|---|---|---|
| Core Syntax Primitive | Uniform prefix list: (op arg1 arg2 ...) | Command + [eager] vs. {deferred string} | Receiver + unary, binary, or keyword1: arg1 keyword2: arg2 |
How It Separates Eager vs. Deferred Execution ([ ... ] vs. ( ... )) | Failed to separate them in base syntax! Both grouping/calls and lists use ( ... ). Wrapping every branch in (lambda () ...) was too noisy, forcing Lisp to invent Special Forms (if, cond, let) and Reader Macros (` , ,@). | Separated [ ... ] (eager substitution) from { ... } (deferred raw string). Great ergonomic leap for Tcl/Tk UI callbacks and expect scripts! | Orthogonal 2-character primitives: ( ... ) is always eager grouping; `[ :x \ |
| Control Flow Status | Special Forms & Macros ({% raw %}defmacro) that suspend standard evaluation rules. | Commands Reparsing Raw Strings (if {...} {...}) via uplevel / upvar dynamic stack injection. | 100% Ordinary Keyword Messages (ifTrue:ifFalse:, whileTrue:, on:do:) sent to Booleans and BlockClosures! Zero control-flow keywords in the grammar. |
| LLM Token & Attention Failure Mode | Positional Slot Ambiguity + Dyck-1 Closing-Parenthesis Drift ())))): At depth 6+, smaller models miscount homogenous closing parens or drop optional &key slots (66.7% on flash-lite in Suite 8). | Dead-String Blindness & Scope Loss: { ... } is an unparsed dead string until runtime, losing static AST validation and lexical closure state (thisContext). | Self-Labeling Keyword Selectors (100.0% in Suite 8): Every argument is labeled at the call site (state:invariant:onSuccess:onFailure:). Dropping onFailure: changes the selector name and triggers instant MessageNotUnderstood. |
Look closely at why Tcl (1988) and Smalltalk-80 (1980) diverged, because both deserve immense historical credit.
When John Ousterhout created Tcl/Tk at UC Berkeley in 1988, and Don Libes built expect at NIST in 1990 on top of Tcl, they saw the flaw in Lisp's overloaded parentheses. They gave Tcl two distinct brackets: [ ... ] for immediate command substitution and { ... } for deferred blocks. That single design choice made button .b -command { ... } in Tcl/Tk and state-machine automation in expect (expect { -re "pat" { ... } timeout { ... } }) a revelation for Unix systems programmers in the early 1990s.
In fact, expect was so useful that in June 1991, I rewrote Don Libes's Tcl expect engine in pure Perl 4 as chat2.pl—a piece of personal history we will visit in Section 3!
So why did we choose 1980 Smalltalk-80 over Tcl for our 2026 agent control plane? Because in Tcl, { ... } is not a parsed AST or a lexical closure—it is an unparsed raw string that gets reparsed from scratch at runtime using uplevel and upvar. In Smalltalk-80, [ :ctx | ... ] is a compiled, lexically scoped BlockClosure object whose syntax fits on the famous Xerox PARC postcard:
"1. Eager grouping (parentheses) vs. Deferred BlockClosure (square brackets):" (2 + 3) * 4. [ :ctx | ctx failingTests > 0 ] value: thisContext. "2. Conditionals, loops, and exception handling are 100% ordinary keyword messages:" aBoolean ifTrue: [ self advanceTo: #Target35Green ] ifFalse: [ self haltWithScar: #SCAR_TDD_01 ]. [ remainingWidgets notEmpty ] whileTrue: [ self generateAndTest: remainingWidgets removeFirst ]. [ self executeTarget20Plan ] on: PremiseRetracted , ScopeDecompositionSignal do: [ :ex | thisContext unwindToFrame: #Target15Archaeology. ex retryUsing: #RenegotiatedPlanB ]. There is no if statement in Smalltalk. There is no for loop keyword. There is no try / catch syntax. Every single one of them is a keyword message send where the selector name is the concatenation of all its colons (#ifTrue:ifFalse:, #whileTrue:, #on:do:).
My 1980s Smalltalk-80 Roots: Tektronix & Steering GemStone Toward Sanity
For me, this wasn't an academic literature search—it was coming home to the very first object-oriented systems I ever touched.
Before I became known as "the Perl guy" (and decades before I became a Dart and Flutter Google Developer Expert), I spent a huge chunk of the 1980s immersed in Smalltalk-80 right here in the Silicon Forest of Oregon:
- Early 1980s at Tektronix (Beaverton, Oregon): Tektronix was one of the original Xerox PARC corporate licensees for Smalltalk-80 (building the legendary Tektronix 4404 artificial-intelligence workstation where Ward Cunningham and Kent Beck later did their pioneering CRC-card and pattern work). Watching Dan Ingalls's live
.image+.changesarchitecture run on early 1980s hardware permanently rewired how I think about software state. - Mid-1980s at Servio Logic (
GemStoneObject Database, 27th Floor of the Georgia-Pacific Building in Downtown Portland): Right after that, I contracted at Servio Logic on the 27th floor of the Georgia-Pacific building in downtown Portland (long before they ever moved out to Beaverton), helping build GemStone—the world's first commercial multi-user Smalltalk object database. As I recounted in my memoir Steering GemStone Toward Sanity, when I arrived, the database query languageOPALwas only "Smalltalk-like," and they had invented bizarre magic punctuation on individual comparison operators (emp.salary @1985-01-01 > 50000) to query historical snapshots of object memory. I stood at the whiteboard and asked two Socratic questions:- "Why does OPAL have to be 'Smalltalk-like'? Why can't it just be standard Smalltalk-80?"
- "And why is
@timestamphacked onto individual<and>operators instead of being a message sent to the entire[ ... ]block closure ([ :emp | emp salary > 50000 ] @ 1985-01-01), so the whole block evaluates inside historical object memory at that point in time?"
Think about that 1985 GemStone insight for a second: evaluating a Smalltalk [ ... ] block closure against an immutable historical snapshot of object memory.
Forty-one years later, when our AI orchestrator wakes up from a mid-flight context compaction, reads <brain>/state/baseline.sources plus <brain>/state/changes-*.changes, rehydrates thisContext, and evaluates invariant: [ :ctx | ... ] against a SHA-256-locked SensorReceipt, it is running the exact same temporal Smalltalk block architecture we designed on the 27th floor of the Georgia-Pacific building in 1985!
3. A 35-Year Detour (91/06/16): How http://chat2.pl Ended Up in Perl 4 Core, Bootstrapped CPAN, Collided with Linux, and Foreshadowed 2026 AI "Chat" Harnesses
Before we look at the Smalltalk-80 StateGraph code and the Suite 8 benchmark numbers, I have to share a 35-year-old story that came up during our architecture review—because if you type http://chat2.pl into your browser right now, it redirects straight to a very specific 1991 commit in the official Perl repository: Perl/perl5@11aea36008/lib/chat2.pl.
Why did I buy the Polish domain chat2.pl years ago and point it at a 1991 Perl 4 library file?
Because back on June 16, 1991 (V2.01.alpha.7 91/06/16), long before "chat" meant typing into an LLM web box, "chatting" in Unix meant writing a state-machine harness to drive an interactive subprocess or TCP socket (telnet, ftp, passwd, uucp) that expected a human at a terminal—waiting for a regex prompt, branching on what came back, handling TIMEOUT and EOF sad paths, and sending the next command.
Don Libes had just written expect in Tcl, and Perl 4 programmers on comp.lang.perl wanted to do the same thing without leaving Perl. So I wrote chat.pl, followed by a multi-handle rewrite called chat2.pl:
## chat2.pl: chat with a server ## V2.01.alpha.7 91/06/16 ## Randal L. Schwartz # Usage: &chat'expect([$handle,] $timeout_time, $pat1, $body1, $pat2, $body2, ... 'TIMEOUT', $timeout_body, 'EOF', $eof_body); Under the hood, chat2.pl took those alternating (pattern, action, 'TIMEOUT', sad_path, 'EOF', eof_path) tuples and dynamically generated and eval-compiled a memoized state-machine subroutine (expectloop000000) on the fly using select(2) bitmasks! Look at that signature: a timeout deadline, positive pattern-match transitions, and mandatory 'TIMEOUT' and 'EOF' sad-path transitions. Thirty-five years ago, chat2.pl was already enforcing SCAR-PROC-152 (out-degree >= 2) on interactive "chat" loops!
How Larry Wall Silently Scooped V2.01.alpha.7 Into Perl Core—and Why # but who the heck would change these anyway? (:-) Met Linux Two Months Later!
Here is the funny part of the story:
- The Silent Scoop Into Perl 4 (
perl 4.0 patch 12, November 5, 1991): I postedchat2.pl(V2.01.alpha.7) to Usenet as an alpha work-in-progress. Without telling me, Larry Wall scooped it right off Usenet and bundled it into the standard Perl 4 core distribution (lib/chat2.pl, commit11aea36008)! It was the only piece of code I ever wrote that ended up inside the core Perl distribution. - Bootstrapping CPAN (
ftp.pland Lee McLoughlin'smirror): Oncechat2.plwas guaranteed to be present in every Perl installation on Earth, Spafford and Lee McLoughlin builtftp.pland the legendarymirrorpackage on top ofchat2.pl. And what didmirrordo? For the entire 1990s,mirror(running on top ofchat2.pl's state-machine loop) was the replication engine that synchronized the global CPAN (Comprehensive Perl Archive Network) mirror fleet! - The 1991
h2phDilemma and the Famous Socket Comment: If you read lines 25–28 oflib/chat2.plin commit11aea36008, you will see one of the most famous comments in Perl history:
unless (socket(S, 2, 1, 6)) { # XXX hardwired $AF_SOCKET, $SOCK_STREAM, 'tcp' # but who the heck would change these anyway? (:-) return undef; } and down in _getpty (line 138):
ioctl(DEVTTY, 0x20007471, 0); # XXX s/b &TIOCNOTTY Why did I hardwire (2, 1, 6) (AF_INET = 2, SOCK_STREAM = 1, IPPROTO_TCP = 6) on June 16, 1991 instead of loading sys/socket.ph?
Because in Perl 4 (years before Perl 5 introduced compiled XS modules like use Socket;), the only way to get C #define constants was if the local system administrator had manually run Larry's h2ph script against /usr/include/*.h—and on 90% of Unix boxes in 1991, either the sysadmin hadn't run h2ph or h2ph had choked on vendor C preprocessor macros! If I had put require 'sys/socket.ph' at the top of chat2.pl, it would have crashed out of the box on 90% of the machines downloading it from Usenet. And on June 16, 1991, across BSD, SunOS, Ultrix, and NeXTSTEP, SOCK_STREAM was 1 everywhere. So I wrote # but who the heck would change these anyway? (:-).
- Two Months Later: Linux and SVR4 Arrive: Exactly two months after I wrote that comment on June 16, 1991, a 21-year-old Finnish student named Linus Torvalds posted to
comp.os.minixannouncing "a different sort of Unix from a new origin"—Linux! Meanwhile, AT&T System V Release 4 (SVR4/ Solaris 2) began spreading withSOCK_STREAM = 2instead of1, and Linux changedTIOCNOTTY(0x5422) and architecture-specific socket constants. Suddenly, my alpha Usenet script—frozen inside Perl core where I didn't even have commit bits—was failing on Solaris and Linux! - The 9-Year Cleanup (
11aea36008→f97f1ee016→a6d7165678→50f338d87d): In July 1996 (perl5.003_02, commitf97f1ee016), the Perl 5 porters wrapped my call ineval { require "sys/socket.ph" }—keeping my# but who the heck would change these anyway? (:-)in theelsebranch—which ironically broke machines with half-generated.phfiles. By July 1999 (a6d7165678), Gurusamy Sarathy slapped a disclaimer at the top ("Use of this library is deprecated. Do not use it as an example of modern Perl"), whileperlfaq8still coyly mentioned it by name. Finally, after I pleaded onperl5-porters(Re: perlfaq8 coyness) to put my 1991 alpha script out of its misery in favor ofExpect.pmandIO::Socket, Jarkko Hietaniemi deleted it on May 15, 2000 in commit50f338d87dwith one of my favorite commit messages in open-source history: >Delete chat2 as requested by Randal
Look at the two timeless lessons of chat2.pl (91/06/16 → 2026):
- First, that
socket(S, 2, 1, 6)story is the textbook proof of our Two-Tier Partition Theorem from Part 4.1 (Sign-Inversion & Environment Drift)! On June 16, 1991, hardwiring(2, 1, 6)to avoid brokenh2phheaders was the+∞pragmatic engineering move for BSD Unix—until two months later, when a brand-new OS kernel called Linux and SVR4 changed the environment constants and flipped it to-∞. You can never freeze environmental assumptions in static weights forever; you need a live sensor. - Second, thirty-five years after I wrote
chat2.plto wrap interactive Unix streams in a deterministic state machine (expectloopwith mandatoryTIMEOUTandEOFsad paths), the software world started "chatting" with LLMs—and forgot that an interactive chat with an intelligent subprocess still requires a deterministic state machine with mandatory sad-path transitions on the outside!
4. The 6-for-1 Smalltalk-80 Unification Architecture & Seaside Continuations
When we asked "How do we keep 1976 Make as our outer dependency graph while unifying all five inner notations (SysV init.d, Graphviz DOT, Gherkin, Belnap tables, and CSP/Flores speech acts) into one zero-ambiguity language?", every piece of 1980 Xerox PARC Smalltalk-80 (plus Avi Bryant's 2002 Seaside continuation framework) clicked into place like precision-machined gears:
flowchart TD Make["🛠️ Outer Topology: 1976 Stuart Feldman Make\n• Stateless pc-free DAG evaluated backward from make finish\n• Physical file prerequisites on disk"] ST["📮 Inner Execution Fabric: 1980 Xerox PARC Smalltalk-80\n• Fits on a postcard (Unary > Binary > Keyword precedence)\n• Zero special forms: [ :ctx | ... ] BlockClosures"] Make --> ST SG["1. StateGraph >> state:invariant:onSuccess:onFailure:\n• Replaces Graphviz DOT + Gherkin\n• Omitting onFailure: triggers compile-time MessageNotUnderstood\n• Enforces SCAR-PROC-152 (out-degree >= 2) by syntax"] IMG["2. thisContext + baseline.sources + changes-*.changes\n• Replaces SysV /etc/init.d Markdown state cards\n• Append-only #storeString delta chunks (!...!) on 3 triggers\n• Smalltalk condenseChanges atomic fold at phase boundaries"] EX["3. BlockClosure >> on:do: (resume: / retryUsing:)\n• Replaces manual rewind & Belnap retraction rules\n• Non-unwinding handler inspects PremiseRetracted / ScopeDecompositionSignal\n• Preserves verified Target 15 SensorReceipts across rewinds"] DNU["4. Performer >> doesNotUnderstand: + Seaside self call:\n• Replaces Hoare CSP (!/?) + Flores role tables\n• Calling #declareApproved: on Performer raises RolePrivilegeViolation\n• Seaside WATask >> self call: yields turn cleanly at Human Gates"] ST --> SG ST --> IMG ST --> EX ST --> DNU Let's inspect the four architectural mechanisms that made this 6-for-1 collapse work in production:
1. Why Make Stays on the Outside While Smalltalk-80 Runs on the Inside
A sharp reader might ask: "Randal, if Smalltalk-80 is so expressive, why keep 1976 Make as the outer target graph? Why not write the outer build graph in Smalltalk too?"
Because Make and Smalltalk-80 govern two fundamentally orthogonal mathematical domains:
-
Make(1976) is apc-free Declarative Dependency Graph over Physical Files:Makehas no program counter (pc = ∅). To find out what to do next,Makewalks backward frommake finishchecking physical file existence and cryptographic hashes on disk. -
Smalltalk-80(1980) is a Reified Activation Frame (thisContext) and Message-Passing State Machine: Inside a target (or across a multi-step loop, anon:do:premise retraction, or a context compaction), you do have an active instruction pointer (pc), a receiver (self), a sender frame (sender), completed vs. remaining loop items (LoopIterationChunk), and live epistemic beliefs (BelnapLedger).
2. Enforcing SCAR-PROC-152 (out-degree >= 2) by Keyword Selector Syntax (StateGraph)
In Part 4.0 and Francisco's agops v2.1, verifying that every state in a plan had both a happy-path transition and a sad-path transition required parsing a Graphviz strict digraph and counting node out-degrees (d^+(v) >= 2). Worse, in English prose or even in Common Lisp with optional keyword arguments (&key on-success on-failure), an LLM under token pressure can quietly omit :on-failure and still produce a syntactically valid S-expression (defstate :target-30 :invariant ... :on-success ...).
In Smalltalk-80, keyword message selectors include every colon-terminated keyword as part of the method name itself. Look at our canonical StateGraph cascade:
StateGraph new state: #Target15Archaeology invariant: [ :ctx | (ctx receiptAt: '15_archaeology_receipt.md') sha256Verified and: [ (ctx belnapAt: #BEL01) ~= #Neither ] ] onSuccess: [ :ctx | ctx advanceTo: #Target20Plan ] onFailure: [ :ctx | ctx haltWithScar: #SCAR_ARCH_01 ]; state: #Target30RedGreenTdd invariant: [ :ctx | (ctx receiptAt: '30_tdd_receipt.md') redWitnessVerified and: [ ctx allTestsGreen and: [ ctx lineCoverage = 100.0 ] ] ] onSuccess: [ :ctx | ctx advanceTo: #Target40Critic ] onFailure: [ :ctx | ctx rewindTo: #Target30RedGreenTdd withScar: #SCAR_TDD_01 ]; yourself. What happens if an LLM tries to cut corners and leaves off onFailure: [ ... ]?
In Smalltalk-80, the parser does not see a call to #state:invariant:onSuccess:onFailure: with a missing fourth argument. It sees a completely different 3-argument selector—#state:invariant:onSuccess:—which does not exist on StateGraph! Before a single line of application code is touched, the Smalltalk evaluator (and the model's own pre-trained Smalltalk grammar head) fires MessageNotUnderstood: StateGraph>>state:invariant:onSuccess:....




