Rewrote the coroutine implementation to use lib/fiber.sx (make-fiber,
fiber-resume, fiber-done?) instead of eagerly running the proc body and
collecting all yields into a list. Each coroutine is now a live fiber —
calls to the coro command invoke fiber-resume, yield suspends via call/cc.
- make-tcl-interp: remove :coroutines/:in-coro/:coro-yields, add :coro-yield-fn nil
- tcl-cmd-yield: calls :coro-yield-fn (fiber's yield fn) to truly suspend
- tcl-cmd-yieldto: same pattern, yields "" to resumer
- make-coro-cmd: takes fiber (not coro-name), calls fiber-resume on each invoke
- tcl-cmd-coroutine: creates a fiber whose body runs the proc with :coro-yield-fn set
- tcl-call-proc result merge: drop :coro-yields/:coroutines propagation
- test.sh: load lib/fiber.sx before lib/tcl/runtime.sx in epoch 4
All 337/337 tests pass including all 20 coro tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parse-int "2.0" returns nil in SX (strict integer parse); fixed by adding
tcl-num-float? (char scan for ./e/E) and tcl-parse-num (routes to
parse-float when float-shaped). Applied in tcl-apply-binop (all arith +
comparisons), tcl-apply-func (parse-float for all math args), unary minus,
and tcl-expr-parse-power (**). Real sqrt/floor/ceil/round/pow/sin/cos/tan/
exp/log now used instead of integer stubs. Integer division still truncates
when both operands are integer-shaped. 329/329 tests green.
Implements namespace eval, current, which, exists, delete, export,
import, forget, path, and ensemble create (auto-map + -map). Procs
defined inside namespace eval are stored as fully-qualified names
(::ns::proc), resolved relative to the calling namespace at lookup
time. Proc bodies execute in their defining namespace so sibling
calls work without qualification.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds lib/tcl/conformance.sh: runs .tcl programs through the epoch
protocol, compares against # expected: annotations, writes
scoreboard.json and scoreboard.md. All 3 classic programs pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 3 headline feature: everything falls out of SX's first-class env chain.
- make-tcl-interp extended with :frame-stack and :procs fields
- proc: user-defined commands with param binding, rest args, isolated scope
- uplevel: run script in ancestor frame with correct frame propagation
- upvar: alias local name to remote frame variable (get/set follow alias)
- global/variable: sugar for upvar #0
- info: level, vars, locals, globals, commands, procs, args, body
- tcl-call-proc propagates updated frames back to caller after proc returns
- test.sh timeout bumped to 90s for larger runtime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add recognizer for expect(await error("HS")).toBe("MSG") pattern in
generate-sx-tests.py, plus eval-hs-error SX helper in the generated
test file. All 18 runtimeErrors tests now generate real test cases
instead of SKIP stubs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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).
Tracks the path from 1277/1496 (85.4%) to 100%. Records each blocker's
fix sketch, files in scope, and order of attack. Cluster #31 spec'd in
detail for the next focused sit-down.
Previous version ran all 7 claude sessions in the main working tree on
branch 'architecture'. That would race on git operations and cross-
contaminate commits between languages even though their file scopes
don't overlap. Now each session runs in /root/rose-ash-loops/<lang> on
branch loops/<lang>, created from the current architecture HEAD.
sx-loops-down.sh gains --clean to remove the worktrees; loops/<lang>
branches stay unless explicitly deleted.
Also: second Enter keystroke after the /loop command, since Claude's
input box sometimes interprets the first newline as a soft break.
sx-loops-up.sh spawns a tmux session 'sx-loops' with 7 windows (lua,
prolog, forth, erlang, haskell, js, hs). Each window runs 'claude'
and then /loop against its briefing at plans/agent-briefings/<x>-loop.md.
Optional arg is the interval (e.g. 15m); omit for model-self-paced.
Each loop does ONE iteration per fire: pick the first unchecked [ ] item,
implement, test, commit, tick, log — then stop. Commits push to
origin/loops/<lang> (safe; not main).
sx-loops-down.sh sends /exit to each window and kills the session.
Attach with: tmux a -t sx-loops
- scripts/loop-guard.sh — atomic claim with 30-min staleness overtake,
appends NDJSON event to .loop-logs/<lang>.ndjson. Exit 0 = go ahead,
exit 1 = another run is live, skip.
- scripts/loop-release.sh — clear lock, log release with exit status.
Intended for 7 per-language /schedule routines firing every 15 minutes.
Lock detects overlap so tight cadences are safe; stale lock (>30 min)
overtaken automatically if an agent dies mid-run.
sx-tree MCP file ops broken this session (Yojson Type_error "Expected
string, got null" on every file-based call — sx_read_subtree,
sx_find_all, sx_replace_by_pattern, sx_summarise, sx_pretty_print, even
sx_load_check on existing files works but summarise fails). Can't edit
integration.sx to add before:init/after:init dispatch. Additionally 4
of the 6 tests fundamentally require stricter parser error-rejection
(add - to currently parses to (set! nil ...); on click blargh end
accepts blargh as symbol expression) — out of single-cluster budget.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cluster 19 was implemented in 4be90bf2 but the plan/scoreboard rows
still marked it pending. Sync the plan state: mark done, add log entry,
bump merged total 1264 → 1277.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Adds regex-compile/test/exec/match-all/replace/replace-fn/split/source/flags.
Opaque dict handle {:__regex__ true :id :source :flags}; compiled Re.re
cached in a primitives-local table. Replacement supports $&, $1-$9, $$.
Flags: i (CASELESS), m (MULTILINE), s (DOTALL). g is a runtime flag handled
in replace. u (unicode) skipped for now.
Unblocks js-on-sx's regex-platform-override! hook — the JS RegExp shim can
now delegate to real regex instead of the substring stub.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements cluster 19 — pick command extensions for hs-upstream-pick suite
(11/24 → 24/24, +13):
- Parser:
- pick items/item EXPR to EXPR supports `start` and `end` keywords
- pick match / pick matches accept `| <flag>` syntax after regex
- pick item N without `to` still works (single-item slice)
- Runtime:
- hs-pick-items / hs-pick-first / hs-pick-last now handle strings
(not just lists) via slice
- hs-pick-items resolves `start`/`end` sentinel strings and negative
indices (len + N) at runtime
- hs-pick-matches added (wraps regex-find-all, each match as a list)
- hs-pick-regex-pattern handles (list pat flags) form; `i` flag
transforms pattern to case-insensitive by replacing alpha chars with
[aA] character classes (Re.Pcre has no inline-flag support)
- Generator:
- extract_hs_expr now decodes JS string escape sequences (\" -> ",
\\ -> \) instead of stripping all backslashes, then re-escapes for
SX. Preserves regex escapes (\d, \s), CSS escapes, and lambda `\`
syntax for String.raw template literals while still producing
correct output for regular JS strings.
Smoke (0-195): 170/195 unchanged (no regressions).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shared formatter in sx_types.ml. Small integer-valued floats still print
as plain ints; floats outside safe-int range (|n| >= 1e16) now print as
%.17g (full precision) instead of silently wrapping to negative or 0.
Non-integer values keep %g 6-digit behavior — no existing SX tests regress.
Unblocks Number.MAX_VALUE / Math.pow(2,N) style tests in js-on-sx where
iterative float loops were collapsing to 0 at ~2^63.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Number.prototype.constructor === Number etc. Four dict-set! lines add
the backlink after each constructor dict is defined.
new String().constructor === String now returns true. Array literals
don't yet link to Array.prototype so [].constructor === Array is still
false — that would need a boxing refactor.
Unit 521/522, slice 148/148 unchanged.
Number 76/100 → 77/100 (+1). String variance-heavy under CPU load.