Five new guest-language plans mirroring the js-on-sx / hs-loop pattern, each with a phased roadmap (Progress log + Blockers), a self-contained agent briefing for respawning a long-lived loop, and a shared restore-all.sh that snapshots state across all seven language loops. Briefings bake in the lessons from today's stall debugging: never call sx_build (600s watchdog), only touch lib/<lang>/** + own plan file, commit every feature, update Progress log on each commit, route shared-file issues to Blockers rather than fixing them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.6 KiB
haskell-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/haskell-on-sx.md forever. Mini-Haskell 98 with real laziness (SX thunks are first-class). Phases 1-3 are untyped — laziness + ADTs first; HM inference is phase 4.
description: haskell-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/haskell-on-sx.md. Isolated worktree, forever, one commit per feature. Never push.
Note: there's an existing /root/rose-ash/sx-haskell/ directory (~25 M). Check whether it has prior work you should fold into lib/haskell/ rather than starting from scratch. Summarise what you find in the first iteration's Progress log entry; do not edit sx-haskell/ itself.
Restart baseline — check before iterating
- Read
plans/haskell-on-sx.md— roadmap + Progress log. - First-run only: peek at
/root/rose-ash/sx-haskell/— does any of it belong inlib/haskell/? Report in Progress log. Don't edit sx-haskell/. ls lib/haskell/— pick up from the most advanced file.- Run
lib/haskell/tests/*.sxif they exist. Green before new work. - If
lib/haskell/scoreboard.mdexists, that's your baseline.
The queue
Phase order per plans/haskell-on-sx.md:
- Phase 1 — tokenizer + parser + layout rule (indentation-sensitive, painful but required per Haskell 98 §10.3)
- Phase 2 — desugar + eager eval + ADTs (
datadeclarations, constructor tagging, pattern matching). Still untyped. - Phase 3 — laziness: thunk-wrap every application arg,
force= WHNF, pattern match forces scrutinee. Classic programs (infinite Fibonacci, sieve of Eratosthenes, quicksort, n-queens, expression calculator) green. - Phase 4 — Hindley-Milner type inference (Algorithm W, let-polymorphism, type-sig checking)
- Phase 5 — typeclasses (dictionary passing, Eq/Ord/Show/Num/Functor/Monad/Applicative,
deriving) - Phase 6 — real
IOmonad backed byperform/resume, full Prelude, drive corpus to 150+
Within a phase, pick the checkbox with the best tests-per-effort ratio.
Every iteration: implement → test → commit → tick [ ] → Progress log → next.
Ground rules (hard)
- Scope: only
lib/haskell/**andplans/haskell-on-sx.md. Do not editspec/,hosts/,shared/, otherlib/<lang>/dirs,lib/stdlib.sx,lib/root, orsx-haskell/. Haskell primitives go inlib/haskell/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 thunks (
make-thunk, force on use) are already in the trampolining evaluator — reuse. Don't invent your own thunk type. - 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.
Haskell-specific gotchas
- Layout rule is the hard bit of parsing — you need a lexer-parser feedback loop that inserts virtual
{,;,}based on indentation. Budget proportionally. - Every application arg is a thunk — compiling
f x yto(f (thunk x) (thunk y))not(f x y). Pattern-match forces. - ADT representation: tagged list, e.g.
data Maybe a = Nothing | Just a→ constructors are(:Nothing)(0-ary) and(:Just <thunk>)(1-ary). Pattern match on the head symbol. - Let-polymorphism (phase 4): generalise at let-binding boundaries only, not at lambda.
- Typeclass dictionaries (phase 5): each class is a record type; each instance builds the record; method call = project + apply.
IO(phase 6): internallyWorld -> (a, World)but in practice backed byperform/resumefor real side effects. Desugardo-notation to>>=.- Out of scope: GHC extensions. No
DataKinds,GADTs,TypeFamilies,TemplateHaskell. Stick to Haskell 98.
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/haskell-on-sx.mdinline. - Short, factual commit messages (
haskell: layout rule + first parse (+10)). - One feature per iteration. Commit. Log. Next.
Go. Read the plan; (first run only) peek at sx-haskell/ and report; find first [ ]; implement.