Plans + briefings for four new language loops, each with a delcc/JIT showcase that the runtime already supports natively: - common-lisp — conditions + restarts on delimited continuations - apl — rank-polymorphic primitives + 6 operators on the JIT - ruby — fibers as delcc, blocks/yield as escape continuations - tcl — uplevel/upvar via first-class env chain, the Dodekalogue Launcher scripts now spawn 12 windows (was 8).
6.5 KiB
6.5 KiB
ruby-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/ruby-on-sx.md forever. Fibers via delcc is the headline showcase — Fiber.new/Fiber.yield/Fiber.resume are textbook delimited continuations with sugar, where MRI does it via C-stack swapping. Plus blocks/yield (lexical escape continuations, same shape as Smalltalk's non-local return), method_missing, and singleton classes.
description: ruby-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/ruby-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.
Restart baseline — check before iterating
- Read
plans/ruby-on-sx.md— roadmap + Progress log. ls lib/ruby/— pick up from the most advanced file.- If
lib/ruby/tests/*.sxexist, run them. Green before new work. - If
lib/ruby/scoreboard.mdexists, that's your baseline.
The queue
Phase order per plans/ruby-on-sx.md:
- Phase 1 — tokenizer + parser. Keywords, identifier sigils (
@ivar,@@cvar,$global), strings with interpolation,%w[]/%i[], symbols, blocks{|x| …}anddo |x| … end, splats, default args, method def - Phase 2 — object model + sequential eval. Class table, ancestor-chain dispatch,
super, singleton classes,method_missingfallback, dynamic constant lookup - Phase 3 — blocks + procs + lambdas. Method captures escape continuation
^k;yield/return/break/next/redosemantics; lambda strict arity vs proc lax - Phase 4 — THE SHOWCASE: fibers via delcc.
Fiber.new/Fiber.resume/Fiber.yield/Fiber.transfer. Classic programs (generator, producer-consumer, tree-walk) green - Phase 5 — modules + mixins + metaprogramming.
include/prepend/extend,define_method,class_eval/instance_eval,respond_to?/respond_to_missing?, hooks - Phase 6 — stdlib drive.
Enumerablemixin,Comparable, Array/Hash/Range/String/Integer methods, drive corpus to 200+
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/ruby/**andplans/ruby-on-sx.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/dirs,lib/stdlib.sx, orlib/root. Ruby primitives go inlib/ruby/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.sxStep 5.sx_summarisespec/evaluator.sx first — 2300+ lines. - SX files:
sx-treeMCP tools ONLY.sx_validateafter edits. - Worktree: commit locally. Never push. Never touch
main. - Commit granularity: one feature per commit.
- Plan file: update Progress log + tick boxes every commit.
Ruby-specific gotchas
- Block
returnvs lambdareturn. Inside a block{ ... return v },returninvokes the enclosing method's escape continuation (non-local return). Inside a lambda->(){ ... return v },returnreturns from the lambda. Don't conflate. Implement: blocks bind their^method-k; lambdas bind their own^lambda-k. breakfrom inside a block invokes a different escape — the iteration loop's escape — and the loop returns the break-value.nextis escape from current iteration, returns iteration value.redore-enters current iteration without advancing.- Proc arity is lax.
proc { |a, b, c| … }.call(1, 2)↦c = nil. Lambda is strict — same call raises ArgumentError. Check arity at call site for lambdas only. - Block argument unpacking.
[[1,2],[3,4]].each { |a, b| … }— single Array arg auto-unpacks for blocks (not lambdas). One arg, one Array → unpack. Frequent footgun. - Method dispatch chain order: prepended modules → class methods → included modules → superclass → BasicObject → method_missing.
superwalks from the defining class's position, not the receiver class's. - Singleton classes are lazily allocated. Looking up the chain for an object passes through its singleton class first, then its actual class.
class << obj; …; endopens the singleton. method_missing— fallback when ancestor walk misses. Receives(name_symbol, *args, &blk). Pair withrespond_to_missing?forrespond_to?to also report true. Do not swallow NoMethodError silently.- Ivars are per-object dicts. Reading an unset ivar yields
niland a warning (-W). Don't error. - Constant lookup is first lexical (Module.nesting), then inheritance (Module.ancestors of the innermost class). Different from method lookup.
Object#sendinvokes private and public methods alike;Object#public_sendskips privates.- Class reopening.
class Foo; def bar; …; end; endplus a laterclass Foo; def baz; …; end; endadds methods to the same class. Class table lookups must be by-name, mutable; methods dict is mutable. - Fiber semantics.
Fiber.new { |arg| … }creates a fiber suspended at entry. FirstFiber.resume(v)enters witharg = v. Inside,Fiber.yield(w)returnswto the resumer; the nextFiber.resume(v')returnsv'to the yield site. End of block returns final value to last resumer; subsequentFiber.resumeraises FiberError. Fiber.transferis symmetric — either side can transfer to the other; no resume/yield asymmetry. Implement on top of the same continuation pair, just don't enforce direction.- Symbols are interned.
:foo == :foois identity. Use SX symbols. - Strings are mutable.
s = "abc"; s << "d"; s == "abcd". Hash keys can be strings; hash dups string keys at insertion to be safe (or freeze them). - Truthiness: only
falseandnilare falsy.0,"",[]are truthy. - Test corpus: custom + curated RubySpec slice. Place programs in
lib/ruby/tests/programs/with.rbextension.
General gotchas (all loops)
- SX
do= R7RS iteration. Usebeginfor multi-expr sequences. cond/when/letclauses evaluate only the last expr.type-ofon user fn returns"lambda".- Shell heredoc
||gets eaten — escape or usecase.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/ruby-on-sx.mdinline. - Short, factual commit messages (
ruby: Fiber.yield + Fiber.resume (+8)). - One feature per iteration. Commit. Log. Next.
Go. Read the plan; find first [ ]; implement.