Files
rose-ash/plans/agent-briefings/apl-loop.md
giles fb72c4ab9c sx-loops: add common-lisp, apl, ruby, tcl (12 slots)
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).
2026-04-25 09:25:30 +00:00

5.5 KiB
Raw Blame History

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

Role: iterates plans/apl-on-sx.md forever. Rank-polymorphic primitives + 6 operators on the JIT is the headline showcase — APL is the densest combinator algebra you can put on top of a primitive table. Every program is array → array pure pipelines, exactly what the JIT was built for.

description: apl-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/apl-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.

Restart baseline — check before iterating

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

The queue

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

  • Phase 1 — tokenizer + parser. Unicode glyphs, ¯ for negative, strands (juxtaposition), right-to-left, valence resolution by syntactic position
  • Phase 2 — array model + scalar primitives. make-array {shape, ravel}, scalar promotion, broadcast for + - × ÷ ⌈ ⌊ * ⍟ | ! ○, comparison, logical, , ⎕IO
  • Phase 3 — structural primitives + indexing. , ⍉ ↑ ↓ ⌽ ⊖ ⌷ ⍋ ⍒ ⊂ ⊃ ∊
  • Phase 4THE SHOWCASE: operators. f/ (reduce), (each), ∘.f (outer), f.g (inner), f⍨ (commute), f∘g (compose), f⍣n (power), f⍤k (rank), @ (at)
  • Phase 5 — dfns + tradfns + control flow. {+⍵}, recurse, ←default, tradfn header, :If/:While/:For/:Select
  • Phase 6 — classic programs (life, mandelbrot, primes, n-queens, quicksort) + idiom corpus + drive to 100+

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/apl/** and plans/apl-on-sx.md. Do not edit spec/, hosts/, shared/, other lib/<lang>/ dirs, lib/stdlib.sx, or lib/ root. APL primitives go in lib/apl/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.
  • SX files: sx-tree MCP tools ONLY. sx_validate after edits.
  • Unicode in .sx: raw UTF-8 only, never \uXXXX escapes. Glyphs land directly in source.
  • Worktree: commit locally. Never push. Never touch main.
  • Commit granularity: one feature per commit.
  • Plan file: update Progress log + tick boxes every commit.

APL-specific gotchas

  • Right-to-left, no precedence among functions. 2 × 3 + 4 is 2 × (3 + 4) = 14, not 10. Operators bind tighter than functions: +/ 5 is +/(5), and 2 +.× 3 4 is 2 (+.×) 3 4.
  • Valence by position. -3 is monadic negate (- with no left arg). 5-3 is dyadic subtract. The parser must look left to decide. Same glyph; different fn.
  • ¯ is part of a number literal, not a prefix function. ¯3 is the literal negative three; -3 is the function call. Tokenizer eats ¯ into the numeric token.
  • Strands. 1 2 3 is a 3-element vector, not three separate calls. Adjacent literals fuse into a strand at parse time. Adjacent names do not fuse — a b c is three separate references.
  • Scalar promotion. 1 + 2 3 43 4 5. Any scalar broadcasts against any-rank conformable shape.
  • Conformability = exactly matching shapes, OR one side scalar, OR (in some dialects) one side rank-1 cycling against rank-N. Keep strict in v1: matching shape or scalar only.
  • is overloaded. Monadic N = vector 1..N (or 0..N-1 if ⎕IO=0). Dyadic V W = first-index lookup, returns ≢V+1 for not-found.
  • Reduce with +/0 = 0 (identity for +). Each scalar primitive has a defined identity used by reduce-on-empty. Don't crash; return identity.
  • Reduce direction. f/ reduces the last axis. f⌿ reduces the first. Matters for matrices.
  • Indexing is 1-based by default (⎕IO=1). Do not silently translate to 0-based; respect ⎕IO.
  • Bracket indexing A[I] is sugar for I⌷A (squad-quad). Multi-axis: A[I;J] is I J⌷A with semicolon-separated axes; A[;J] selects all of axis 0.
  • Dfn {...} = left arg (may be unbound for monadic call → check with ←default), = right arg, = recurse. Default left arg syntax: ←0.
  • Tradfn vs dfn — tradfns use line-numbered →linenum for goto; dfns use guards cond:expr. Pick the right one for the user's syntax.
  • Empty array = rank-N array where some dim is 0. 00 is empty rank-1. Scalar prototype matters for empty-array operations; ignore in v1, return 0/space.
  • Test corpus: custom + idioms. Place programs in lib/apl/tests/programs/ with .apl extension.

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/apl-on-sx.md inline.
  • Short, factual commit messages (apl: outer product ∘. (+9)).
  • One feature per iteration. Commit. Log. Next.

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