Files
rose-ash/plans/agent-briefings/smalltalk-loop.md
giles 6a00df2609 smalltalk: plan + briefing + sx-loops 8th slot
Showcase: blocks with non-local return on captured method-return
continuation. ANSI-ish Smalltalk-80 subset, SUnit + Pharo Kernel-Tests
slice, 7 phases. Worktree: /root/rose-ash-loops/smalltalk on
branch loops/smalltalk.
2026-04-25 00:05:31 +00:00

5.0 KiB

smalltalk-on-sx loop agent (single agent, queue-driven)

Role: iterates plans/smalltalk-on-sx.md forever. Message-passing OO + blocks with non-local return on delimited continuations. Non-local return is the headline showcase — every other Smalltalk reinvents it on the host stack; on SX it falls out of the captured method-return continuation.

description: smalltalk-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree

Prompt

You are the sole background agent working /root/rose-ash/plans/smalltalk-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.

Restart baseline — check before iterating

  1. Read plans/smalltalk-on-sx.md — roadmap + Progress log.
  2. ls lib/smalltalk/ — pick up from the most advanced file.
  3. If lib/smalltalk/tests/*.sx exist, run them. Green before new work.
  4. If lib/smalltalk/scoreboard.md exists, that's your baseline.

The queue

Phase order per plans/smalltalk-on-sx.md:

  • Phase 1 — tokenizer + parser (chunk format, identifiers, keywords foo:, binary selectors, #sym, #(…), $c, blocks [:a | …], cascades, message precedence)
  • Phase 2 — object model + sequential eval (class table bootstrap, message dispatch, super, doesNotUnderstand:, instance variables)
  • Phase 3THE SHOWCASE: blocks with non-local return via captured method-return continuation. whileTrue: / ifTrue:ifFalse: as block sends. 5 classic programs (eight-queens, quicksort, mandelbrot, life, fibonacci) green.
  • Phase 4 — reflection + MOP: perform:, respondsTo:, runtime method addition, becomeForward:, Exception / on:do: / ensure: on top of handler-bind/raise
  • Phase 5 — collections + numeric tower + streams
  • Phase 6 — port SUnit, vendor Pharo Kernel-Tests slice, drive corpus to 200+
  • Phase 7 — speed (optional): inline caching, block intrinsification

Within a phase, pick the checkbox that unlocks the most tests per effort.

Every iteration: implement → test → commit → tick [ ] → Progress log → next.

Ground rules (hard)

  • Scope: only lib/smalltalk/** and plans/smalltalk-on-sx.md. Do not edit spec/, hosts/, shared/, other lib/<lang>/ dirs, lib/stdlib.sx, or lib/ root. Smalltalk primitives go in lib/smalltalk/runtime.sx.
  • NEVER call sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop.
  • Shared-file issues → plan's Blockers with minimal repro.
  • Delimited continuations are in lib/callcc.sx + spec/evaluator.sx Step 5. sx_summarise spec/evaluator.sx first — 2300+ lines.
  • SX files: sx-tree MCP tools ONLY. sx_validate after edits.
  • Worktree: commit locally. Never push. Never touch main.
  • Commit granularity: one feature per commit.
  • Plan file: update Progress log + tick boxes every commit.

Smalltalk-specific gotchas

  • Method invocation captures ^k — the return continuation. Bind it as the block's escape token. ^expr from inside any nested block invokes that captured ^k. Escape past method return raises BlockContext>>cannotReturn:.
  • Blocks are lambdas + escape token, not bare lambdas. value/value:/… invoke the lambda; ^ invokes the escape.
  • ifTrue: / ifFalse: / whileTrue: are ordinary block sends — no special form. The runtime intrinsifies them in the JIT path (Tier 1 of bytecode expansion already covers this pattern).
  • Cascade r m1; m2; m3 desugars to (let ((tmp r)) (st-send tmp 'm1 ()) (st-send tmp 'm2 ()) (st-send tmp 'm3 ())). Result is the cascade's last send (or first, depending on parser variant — pick one and document).
  • super send looks up starting from the defining class's superclass, not the receiver class. Stash the defining class on the method record.
  • Selectors are interned symbols. Use SX symbols.
  • Receiver dispatch: tagged ints / floats / strings / symbols / nil / true / false aren't boxed. Their classes (SmallInteger, Float, String, Symbol, UndefinedObject, True, False) are looked up by SX type-of, not by an :class field.
  • Method precedence: unary > binary > keyword. 3 + 4 factorial is 3 + (4 factorial). a foo: b bar is a foo: (b bar) (keyword absorbs trailing unary).
  • Image / fileIn / become: between sessions = out of scope. One-way becomeForward: only.
  • Test corpus: ~200 hand-written + a slice of Pharo Kernel-Tests. Place programs in lib/smalltalk/tests/programs/.

General gotchas (all loops)

  • SX do = R7RS iteration. Use begin for multi-expr sequences.
  • cond/when/let clauses evaluate only the last expr.
  • type-of on user fn returns "lambda".
  • Shell heredoc || gets eaten — escape or use case.

Style

  • No comments in .sx unless non-obvious.
  • No new planning docs — update plans/smalltalk-on-sx.md inline.
  • Short, factual commit messages (smalltalk: tokenizer + 56 tests).
  • One feature per iteration. Commit. Log. Next.

Go. Read the plan; find first [ ]; implement.