Technical Museum
All Hardware Maps ML & AI Music Social Tools Writing Apps & Games
Deciduous
Decision graph CLI for tracing how software decisions evolve
Rust SQLite HTML/JS
Deciduous Archaeology Demo
Demonstrations of decision archaeology on React and stacked git workflows
Rust React Git
Phish Explorer
Jam analytics dashboard for Phish 3.0 era
Elixir Phoenix LiveView D3.js
Local LLM on MacBook
4-bit quantization, safetensors, and Bumblebee + EMLX for Apple Silicon
Elixir Rust Python
A Bot that posts like me
Porting the posting bot to Elixir using local LLM work
Elixir Bumblebee EMLX
Receipt Printer Software Suite
A complete software suite for thermal receipt printers
Elixir Python
Role Call
TV writer overlap explorer
Elixir Phoenix LiveView
Fill Your Sky
Interactive map of 418+ Bluesky communities with 545K+ people
Elixir Phoenix LiveView D3.js
Code Mirror
A live code mirror experiment
Elixir Phoenix LiveView
Pocket Pnin
A local LLM running on my iPhone, coming to the App Store for free
Swift MLX
NYC Census Maps
Interactive census and PLUTO data visualization for New York City
Elixir Phoenix LiveView Leaflet.js
MTA Bus Tracker
Real-time MTA bus and train tracking on an interactive map
Elixir Phoenix LiveView Leaflet.js
Concert GIF Maker
Extract GIFs from concert videos with a retro Mac interface
Elixir Phoenix LiveView FFmpeg
Send a VERY direct message, to my receipt printer
A social project where friends send photos that print on my receipt printer
Elixir Phoenix LiveView
Archive TV
A real over-the-air TV channel from magnetic media archives
Elixir FFmpeg
Losselot
Neural network loss function explorer
Python
Todoinksies
A personal todo app
Elixir Phoenix LiveView
Ormery
An ORM written in Temper
Temper
Collage Maker
Upload photos and arrange them into grid collages
Elixir Phoenix LiveView
GenStage Tutorial 2025
A modern GenStage tutorial for the Elixir ecosystem
Elixir GenStage
Temper Rust Bug
Found a bug in the Temper compiler and built a demo repo
Temper Rust
300+ Years of Tree Law
A blog post that became its own LiveView application
Elixir Phoenix LiveView
HEEx in Other Languages
Experiments porting Phoenix HEEx templates to Rust, Lua, C#, Java, Python, and JS
Temper Rust Lua
Live Draft LSP
Live-stream blog drafts from Zed to Phoenix via a custom LSP
Rust Elixir Zed
Bluesky Hoover Apps
Various apps that vacuum up and process the Bluesky firehose
Elixir Phoenix LiveView
Bobby Posts Bot
A bot that posts like me, in Python
Python
Photo Booth Receipt Printer
A portable photo booth that prints on receipt paper
Python Elixir
Nathan For Us
A Nathan For You social network with video search and GIF creation
Elixir Phoenix LiveView FFmpeg
Browser History Roast MCP
An MCP server that roasts you based on your browser history
Python MCP
GenStage Tutorial (Original)
The original GenStage tutorial
Elixir GenStage
Bluesky Firehose Toys
Real-time firehose visualizations: emoji streams, jetstream comparisons, and more
Elixir Phoenix LiveView WebSocket
31 exhibits Apps & Games
MY FAV
LEICA SHOTS
Work Log — Recent Commits
+2111-1451 notactuallytreyanastasio/blimp/main Apr 24 21:00
ebf44cb chore(deploy): point rsync at /opt/blimp and follow symlinks The Caddy container (blog-caddy-1 on the Hetzner box) mounts /opt/blimp:/srv/blimp:ro, which means the host path Caddy actually reads from is /opt/blimp. The old deploy.sh rsynced to /srv/blimp on the host, which the Caddy container never saw, so content looked like it was transferring successfully but 404'd at blimp.bobbby.online. Fixes the DEPLOY_DIR to /opt/blimp and adds -L to the rsync flags so the docs/tutorial/exercises symlink gets followed and the real .blimp files end up on the server. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90a90fd docs: scrub 'Ruby's skin, Elixir's soul' phrase Removes the pithy motto from the three places it appeared (the core language feel design doc, the parser design doc, and the manifesto) and replaces each with a direct description of what the phrase meant: Blimp's surface syntax is Ruby-flavored (do/end, optional parens, readable sentences) and the data model is Elixir-flavored (immutable values, pattern matching, pipes, atoms). Same content, no marketing motto. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
00efd1a feat(lang): blimp_run_tests WASM export + structured test reports Adds a `blimp_run_tests(source_ptr, source_len)` export to the WASM API that parses source, runs every `test` block found in actors, and emits a JSON report with per-test pass/fail plus the expected/actual detail captured from any failing assertion. Assertion builtins now write to a shared `last_assertion_detail` buffer on failure so the runner can attach a readable error to each failing test. Also fixes three pre-existing `usize`/`u64` mismatches in builtins.zig that were blocking wasm32 builds under current Zig, and adds a design doc at docs/lang_design/test-framework.md that lays out what a first-class Blimp test framework should grow into (watch mode, structural diffs, source lines on failure, property shrinking). The browser-embeddable interpreter at docs/blog/repl/blimp.js gets a new `runTests(source)` method that wraps the export and returns `{ok, total, passed, failed, tests: [...]}`. Used by the tutorial SPA to give readers red/green feedback inline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9d20b71 Merge lang/ws-bidirectional: WASM test runner, bike share tutorial, SPA, deploy fix # Conflicts: # deploy.sh
d939fdf feat(tutorial): SPA with embedded REPL, test runner, and Blimp highlighter Adds docs/tutorial/index.html, a single-page tutorial viewer. Three-pane layout: chapter rail on the left, rendered markdown in the middle, interactive REPL pane on the right. URL fragment (#ch00, #ch01, etc) drives chapter navigation, fetches the chapter's markdown, loads the matching exercise stub into the editor, and runs the reader's code against the WASM-compiled Blimp runtime via the new `runTests()` binding. Failures show up inline with structural detail captured from the assertion builtin. "Cheat" tab fetches the solution .blimp file from disk and highlights it; "Copy to editor" paves over the stub. Also adds blimp-highlight.js, a zero-dependency syntax highlighter for Blimp. Keyword list is derived from chunks/lang/tree-sitter-blimp/ grammar.js plus Zig-parser extras (test, property, given, if, else) so it stays in sync with the real grammar. Handles string interpolation (#{...}), atoms, numbers, operators, keywords, builtins, and type-like capitalized identifiers. Used for code blocks in rendered chapter markdown, the live editor textarea (layered <pre> overlay with scroll sync), and the cheat-view solution display. exercises/ lives at the repo root so CLI users can run `blimp test exercises/...`. For the web tutorial, docs/tutorial/exercises is a symlink to ../../exercises so the SPA fetches work from a single path both locally and in production; deploy.sh follows the symlink via rsync -L. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79aad67 chore: remove old blog tutorial.html The old interactive browser tutorial at docs/blog/tutorial.html is replaced by the new bike-share tutorial under docs/tutorial/. No incoming links remain from blog/index.html or the manifesto. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9a4e116 feat(chat): poll-based server loop, read WS frames end-to-end Replace the blocking accept_loop with a poll-based loop that watches [server_fd | all_ws_fds]. When a WS fd is ready, read one frame and route {"type":"msg","from":..,"text":..} to the room, then broadcast. HTTP POST /send path preserved as a fallback for WS-less clients. Also fixes extract_quoted's off-by-one: Blimp evaluates args such that char_at(s, idx) in a call alongside idx+1 sees the post-incremented idx. Compute char_at into a local first. Same pattern may need the same treatment wherever it appears (json_escape_loop, slice_idx); not swept in this commit. TODO: step 3 of the bidirectional-WS plan still has 'from' coming from the client frame instead of a server-side session lookup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
00f8e54 feat(tutorial): bike share tutorial chapters 0-2 and first exercises Builds a TDD-driven Blimp tutorial that teaches the actor model by constructing a bike share simulation one actor at a time. Twelve chapters planned total; this commit lands the first three: - Chapter 0: why actors, the dispatcher-with-clipboard mental model, functional core / imperative shell as the organizing philosophy, and the piecemeal progression the rest of the chapters follow. - Chapter 1: your first actor. A Bike with id and status, four handlers filled in one at a time. Walks through actor templates, state defaults, the `::` assignment operator, atoms as interned symbols, spawn-with-overrides, the `<-` send operator, and why `become` exists (Hewitt's original actor primitive, not mutation). - Chapter 2: state, become, and the free lock. Multi-clause handlers with guards, tagged `{:ok, ...}` and `{:error, ...}` tuples, a third `:broken` state that drops in without any type edits, and the punchline of the chapter: why an actor's mailbox plus "handlers run to completion" gives you mutual exclusion without any locks. Each chapter has a matching exercise file in exercises/chNN_topic/ with a stub the reader fills in, failing tests that progressively go green, and a reference solution in solutions/. Voice: one-sentence-per-line markdown, no em-dashes, piecemeal concept-then-exercise flow, design decisions traced back to the deciduous graph and docs/lang_design/ where they were made. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+1599-14850 notactuallytreyanastasio/blimp/main Apr 24 19:10
8bfe1a3 infra: deploy blimp docs to /opt/blimp (#21) * V1 of the new terminal UI * attempt to get a faster commit dialogue and make Follow mode work * feat: C++/Notcurses diff viewer -- Phase 1 scaffold Port blimp_diff from Rust/Ratatui to C++20/Notcurses for direct cell manipulation and zero widget-tree allocation per frame. Architecture (mirrors the Rust version): - git/: Runner (popen), status/diff/log parsers, file watcher - state/: Navigation, Interaction (mode + action mapping), CommitState, LineSelection, FileState classifier - ui/: Renderer with direct ncplane cell writes, file list, diff pane with gutter + colored lines, status bar, commit overlay, 4 themes (solarized dark/light, monokai dark/light) Performance: - Async refresh: watcher-triggered git calls run in background thread via std::async, polled non-blocking each frame. Navigation is pure index math against cached data -- zero git calls on j/k - All untracked file diffs pre-cached on refresh - Input: 16ms poll timeout, drain all pending events before render, filter NCTYPE_RELEASE to prevent double-skip Terminal hygiene: - Clean shutdown: watcher thread joined before notcurses teardown, stdin drained in raw mode to eat in-flight escape responses, explicit reset sequences for mouse/kitty keyboard/bracketed paste - US keyboard shift map for kitty keyboard protocol (Shift+= gives +) Commit overlay supports multi-line bodies (Enter = newline, Ctrl+Enter = submit). 54 unit tests covering all parsers, state machines, navigation, selection, and file state classification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Phase 2 -- syntax highlighting, follow mode, log view Syntax highlighting: - Built-in regex tokenizer (no external deps) covering keywords, strings, comments, numbers, operators, types (CamelCase), functions - Language detection from extension (C/C++, Rust, Python, JS/TS, Go, Ruby, Elixir, Zig, Shell, Lua, Java, C#, CSS, HTML, YAML, TOML, JSON) - Per-language keyword sets with theme-derived colors - DiffCache pre-renders highlighted spans once per file switch; frame rendering just reads the cache. 50k line cap for huge files. Follow mode: - apply_refresh() detects changed files and calls follow_jump() - Watcher-triggered async refresh auto-scrolls to changed file Log view: - Renders actual git log entries with colored hash + message - Virtual scroll with selected-item highlight, j/k nav, q to return Diff pane rewritten to consume DiffCache spans via batched ncplane_putstr_yx (one call per span, not per character). Renderer signature updated to pass DiffCache and log entries. 14 new highlight tests (68 total, all green). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: notcurses guide audit -- signal handling, resize, perf Informed by reading Nick Black's "Hacking the Planet with Notcurses" cover to cover and building a 90-node deciduous knowledge graph. Signal handling: - NCOPTION_NO_QUIT_SIGHANDLERS disables notcurses' own handlers (they race with our cleanup) - Own sigint_handler sets atomic flag, event loop exits cleanly - Terminal state saved with tcgetattr BEFORE notcurses_init, force-restored with tcsetattr(TCSAFLUSH) AFTER notcurses_stop - Stdin drained in raw mode to eat in-flight escape sequences Resize: - NCKEY_RESIZE now handled: clears diff cache, re-queries dims - Ctrl+L calls notcurses_refresh() for full screen redraw Performance: - hline() uses ncplane_hline (one call, not per-cell loop) - vline() uses ncplane_vline (one call, not per-cell loop) - fill_rect() uses ncplane_hline per row with shared nccell - ncplane_erase() for full-screen clear Init: - setlocale(LC_ALL, "") in main() -- mandatory per notcurses docs 68 tests green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: commit overlay as its own ncplane Per the notcurses guide (Ch5/Ch14 tetris architecture), modal overlays should be separate planes composited by notcurses, not painted directly onto the standard plane. This means the background content is preserved and doesn't need full re-rendering when the overlay is dismissed. - create_overlay_plane() creates a child plane of stdplane, centered, sized to 60%x40% of terminal. Named "commit-overlay". - show_overlay()/hide_overlay() manage the plane lifecycle - Overlay created on cc chord and amend ('a'), destroyed on cancel (Esc) and successful commit - NCKEY_RESIZE destroys and recreates the overlay at new size - Overlay coordinates are now relative to its own plane (0,0), not the terminal -- cleaner rendering code - Shutdown path destroys overlay before notcurses_stop() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: visual select → Claude agent seam Select diff lines, ask Claude a question, get a streaming response in a full-screen agent tab. Flow: - v in diff view: enter visual selection mode (j/k to extend) - Enter in selection: captures selected lines, opens agent prompt overlay where you type your question - Ctrl+Enter: spawns claude --no-input as a persistent subprocess with pipes, sends initial prompt (selected code + question), switches to full-screen AgentView tab - AgentView: streams response with auto-scroll, j/k to scroll, Tab to flip back to diff (agent keeps running), Esc to dismiss - g from file list or diff view: switch to agent tab Architecture: - AgentState manages the subprocess lifecycle (fork/exec/pipes), reader thread for non-blocking stdout consumption, mutex-guarded response buffer. Supports send_followup() for ongoing conversation. - AgentView is a full-screen mode (like log view), not an overlay - Agent prompt reuses the overlay plane system - extract_selected_lines() reconstructs +/- prefixed text from the DiffCache spans Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: eliminate large-file freeze with lazy per-frame highlighting The old DiffCache pre-highlighted every line of the entire diff on file switch. For large files (1000+ lines), the tokenizer blocked the event loop for hundreds of milliseconds, freezing the UI. New approach: DiffCache stores lightweight CachedLines with raw text only (no Span vectors). The diff pane renderer calls highlight_line() on the fly for just the ~40-50 visible lines per frame. Switching files is now O(n) string copies with no tokenization -- effectively instant even for 50k-line diffs. Scrolling is pure index math. Highlighting cost is bounded by visible_height * tokenizer_cost_per_line, which is ~50 microseconds total per frame on a typical terminal. Side benefit: theme cycling no longer needs to invalidate the cache since highlighting derives colors from the current theme each frame. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace std::regex with manual parser, async initial load Root cause of large-file freeze: std::regex_search() was called on EVERY line of diff output to check for hunk headers. libc++ regex is notoriously slow -- for 5837 lines (not_curses.txt), thousands of failed regex matches dominated parse time. Fix: manual parse_hunk_header() using strtol(). Only called when line starts with "@@" (fast 2-byte prefix check). Regex removed entirely from the diff parser. Also: initial data load is now async (refresh_async instead of refresh_sync). UI appears immediately while git commands run in the background. Previously the app would freeze for seconds on startup in repos with large untracked files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: double Ctrl+C force-exits, lazy untracked diffs, no pre-diffing Three fixes for the freeze-and-can't-exit problem: 1. Signal handler now force-exits on SECOND Ctrl+C: first sets the atomic flag (polite), second calls notcurses_stop + tcsetattr + _exit directly (nuclear). This guarantees escape even when notcurses_render() is blocked writing to a slow terminal. 2. Removed pre-diffing of ALL untracked files in build_refresh(). git diff --no-index on large untracked files (5837-line not_curses.txt) was blocking the background thread for seconds. Untracked diffs are now fetched lazily only when the user navigates to that specific file. 3. Global notcurses pointer + saved termios for signal handler emergency cleanup path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: stop full-screen erase every frame, use notcurses damage tracking ROOT CAUSE of scroll freeze: ncplane_erase() was called every frame, marking ALL ~10,000 cells as damaged. notcurses_render() then emitted escape sequences for every cell, even unchanged ones. Switching from a 5-line diff to a 455-line diff meant ~8000 RGB color changes at ~14 bytes each = ~112KB of escape sequences in one frame. Fix: only erase on mode transitions, resize, theme change, or data refresh (needs_full_redraw_ flag). Normal j/k navigation in the file list just overwrites the visible rows -- each pane fills its own area via hline(). Notcurses damage tracking now properly skips unchanged cells between frames. Added switch_mode() helper that sets both the mode and the dirty flag, replacing 17 direct set_mode() calls. This is the optimization the notcurses guide (Ch4.3) describes: "Subsequent to the first call, screen updates will write only to changed (damaged) cells" -- but only if you don't erase them first. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: infinite loop in syntax highlighter on # character ROOT CAUSE of the scroll freeze. The tokenizer had an infinite loop: for Rust files, # (used in #[derive], #![...] attributes) was not handled by any branch. The "everything else" fallback loop stopped at # but didn't advance i. The outer while loop restarted with the same i, checking the same character forever. The file causing the freeze was app.rs (+455 lines) -- every line with a # attribute spun the tokenizer infinitely. Fix: 1. Added explicit # handler: for C/C++/C# it's preprocessor (rest of line). For Rust it consumes #[...] or #![...] as keywords. For Python/Ruby/etc it was already caught by is_comment_start. 2. The "everything else" fallback now ALWAYS advances i by at least 1 before entering its while loop. This is a safety net -- no character can ever cause an infinite loop regardless of language. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: commit overlay now opaque -- set base cell bg before erase The overlay plane was transparent because ncplane_erase() fills cells with the base cell, not the active fg/bg. The base cell defaulted to no explicit background, so the diff pane bled through underneath. Fix: set the overlay plane's base cell to the theme bg_selected color with NCALPHA_OPAQUE before erasing. Now the overlay fully covers the content underneath, as the notcurses guide (Ch8) describes: "the base cell is rendered wherever the plane's gcluster is 0." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Commit rust stuff so its here for reference while we do C++ impl * fix: segfault from infinite recursion in switch_mode, shell quoting Two bugs: 1. SEGFAULT: switch_mode() called itself instead of interaction_.set_mode(). The replace-all of set_mode→switch_mode caught the call INSIDE switch_mode's own body, creating infinite recursion → stack overflow → segfault on any mode transition. 2. SHELL INJECTION: runner's arg quoting only triggered on spaces/quotes. Commit messages with newlines, $, backticks etc could break or be interpreted by the shell. Now ALL args are unconditionally single-quoted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add useless file * test: comprehensive test suite -- 68 → 200 tests 132 new tests covering every module with edge cases and integration tests using real git repositories. Parser edge cases (26 new): - Status: deleted, MM, spaces in paths, empty lines, renamed+modified - Diff: no-newline marker, new/deleted files, multi-file, count=1 hunks, very long lines, --no-index format, function context headers - Log: special chars, unicode, long messages, full-length hashes Highlight edge cases (20 new): - Rust #[derive] attributes, lifetimes ('a) - Go/Elixir/Ruby keywords and comments - Nested/escaped/empty strings, operators-only, whitespace-only - 1200-char lines, C preprocessor, hex/binary/float numbers - Function call and CamelCase type detection - Language detection for all supported extensions State machine edge cases (51 new): - Navigation: empty lists, bounds clamping, scroll extremes, hunk/log boundaries, follow jump with/without mode, divider extremes - Interaction: all DiffView/Selecting/AgentView key mappings, cc chord timing (within/after 400ms, c+different key), shift non-interference - Commit: double submit, insert-after-cancel, error→retry, long messages, newlines-only, idle no-ops - Selection: single-point, range-after-clear, extend-without-start, multiple extends, empty-contains - FileState: None/None fallback, already-staged/unstaged nullopts, command arg verification Git integration tests (17 new): - Real temp repos via mkdtemp + git init with RAII cleanup - Runner: status/diff/commit/log/stage/unstage round-trips - E2E: runner → parser → DiffCache → FileState classification - Verifies actual git subprocess output parsing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: black screen on startup -- switch back to sync initial load The async initial load left repo_.files empty on the first frame, rendering a blank dark screen until the background thread completed. Switch back to refresh_sync() for the initial load so the UI has data immediately. Subsequent watcher-triggered refreshes remain async. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: kqueue-based file watcher replaces polling The old watcher only checked .git/index and .git/HEAD mtimes, missing all working tree changes (file edits, new files). Then it was changed to poll git status every 600ms which was wasteful. New watcher uses kqueue (macOS) to get instant filesystem notifications: - Watches .git/index, .git/HEAD, .git/refs for git operations - Watches working tree directories (up to 4 levels deep) for file edits - Skips .git, build, target, node_modules, .deciduous - kqueue EVFILT_VNODE with NOTE_WRITE|DELETE|RENAME|ATTRIB - Debounces rapid changes (save-all) with configurable delay - Drains accumulated events after debounce to coalesce - Zero CPU when idle (blocked in kevent syscall, not polling) File edits now trigger a refresh within ~200ms instead of never. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: x to discard file changes, S to stash x key (DiscardFile): - Tracked files with unstaged changes: git checkout -- <path> - Untracked files: deletes the file from disk - No-op on clean files or staged-only files S key (Stash): - Runs git stash, refreshes immediately - Status bar shows "Stashed" or "Stash failed" Also added stash_pop() to runner for future use. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * simplify: u always runs git restore --staged Replaced the branching unstage logic (rm --cached for new files, restore --staged for others) with a single git restore --staged call. Simpler and works for all staged files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: crash on s/u/x -- reference to repo_.files after refresh do_stage, do_unstage, and do_discard all referenced repo_.files[idx] AFTER calling refresh_sync(), which replaces repo_.files entirely. If the file list shrank (e.g., staging the last changed file), the old index was out of bounds -> segfault. Fix: copy the path (and any needed status fields) to local variables before calling refresh_sync(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: split staged/unstaged diff display for partially staged files When a file has both staged and unstaged changes (MS status), the diff pane now shows them as separate sections with headers: ── Staged ── @@ ... @@ (what will be committed) ── Unstaged ── @@ ... @@ (working tree changes not yet staged) Data model: replaced FileDiff with CombinedDiff which holds separate staged and unstaged FileDiff structs. merge_diffs no longer blindly concatenates hunks -- it preserves the distinction. Section headers only appear when BOTH staged and unstaged exist for the same file. Single-state files render normally without headers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: kitty keyboard protocol garbage on quit The execute: 3u[107;;107u... garbage after quitting was kitty keyboard protocol responses arriving after the shell had already started reading stdin. The terminal processes our disable sequence and sends an acknowledgment, but it arrives too late. Fix: send kitty keyboard + mouse disable sequences BEFORE notcurses_stop(), wait 50ms for terminal to process them, drain responses via notcurses_get, THEN tear down. Final 200ms raw-mode drain (up from 100ms) catches any stragglers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: flash messages at top of diff pane, auto-clear after 2s Status messages (Staged, Unstaged, Discarded, Stashed, etc.) now appear as a highlighted bar at the top of the diff pane instead of in the status bar where they covered keyboard shortcuts. - set_flash() stores message + expiry timestamp (now + 2 seconds) - active_flash() returns the message if not expired, empty otherwise - Rendered as accent-colored bar spanning the diff pane width - Auto-clears after 2 seconds with no manual dismissal needed - Removed status_message_ field entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: black lines between hunks -- fill entire row before gutter/content Context lines between hunks appeared as black gaps because only the content area was cleared with hline, but stale cells from previous frames (before the no-full-erase optimization) persisted in the gutter region. Now each diff line fills the entire row width with line_bg first, then overwrites with gutter + content. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: selection highlight now clearly visible across all themes Selection bg was nearly identical to the normal bg in all themes (solarized dark: {7,54,66} vs bg {0,43,54} -- invisible difference). New selection colors use a strong blue tint that's clearly distinct from both the normal bg and the diff add/del backgrounds: - Solarized Dark: {20, 70, 110} (dark blue) - Solarized Light: {180, 210, 240} (light blue) - Monokai Dark: {50, 70, 110} (dark blue) - Monokai Light: {170, 200, 240} (light blue) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: follow mode bouncing, centered diff scroll, opaque agent overlay Three fixes: 1. Follow mode no longer bounces between existing files. It now compares old vs new file lists and only jumps when a genuinely NEW file appears (wasn't in previous snapshot). Previously it jumped to any non-selected file with a diff on every refresh. 2. Diff scroll now centers the cursor in the viewport. Added diff_cursor_ separate from diff_scroll_ -- cursor moves with j/k, scroll auto-adjusts to keep cursor at viewport midpoint. Selection extends from cursor position, not scroll position. 3. Agent prompt overlay now opaque (same base cell fix as commit overlay). Uses larger plane (80%x70% via create_agent_plane). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: claude subprocess uses -p flag, cursor line visible in diff pane Claude invocation: - Replaced broken fork/exec with --no-input (flag doesn't exist) with simple popen of 'claude -p'. Prompt written to temp file and piped via cat to avoid shell escaping issues with code content. - Simplified AgentState: removed fork/pipe/pid fields, uses std::thread + popen instead. Diff pane cursor: - Cursor line now highlighted with bg_selected when diff pane is focused (not during active selection to avoid confusion). - Gutter background also highlights on cursor line. - Cursor visible immediately on entering diff view, before any selection starts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: diff scroll/cursor resets when switching files The centered-cursor scroll change introduced diff_cursor_ but never reset it on file switch. Navigating between files in the file list left the cursor at the previous file's position, making the diff appear stuck or empty. Added reset_diff_scroll() which zeroes cursor, scroll, and h_scroll. Called in rebuild_diff_cache when the selected file changes. Also reset cursor in follow_jump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: diff pane now actually focused when in DiffView mode switch_mode was setting the interaction mode to DiffView but not updating the active pane to Diff. The diff pane renderer checks active_pane for the focused flag, which controls cursor visibility and border color. Mode was DiffView (keys worked) but pane was still FileList (cursor invisible, border dim). Fix: switch_mode now auto-syncs active pane with mode: - DiffView/Selecting → Pane::Diff - FileList → Pane::FileList Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * finally scrolling right * kill the diff viewing stuff with fire, we might as well just write a Zed plugin for that! * docs: add Blimp Chat code walkthrough End-to-end walk through chunks/lang/web/chat.blimp: the four actors, the accept loop, WS handshake, broadcast mechanism, JS client, and the Zig TCP/WS primitives that back them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * infra: deploy blimp docs to /opt/blimp to match static-site convention The Caddy container in ~/code/blog mounts /opt/<name>:/srv/<name>:ro for deciduous, phstats, donny, and now blimp. Switching the rsync target from /srv/blimp to /opt/blimp aligns with that pattern and makes the deploy flow self-consistent. Fixes the outage on blimp.bobbby.online that went unnoticed from 2026-04-05 until today -- a prior server-local Caddy hotfix pointing at /srv/blimp was overwritten by a blog deploy, taking the subdomain offline. The matching Caddyfile + docker-compose changes land in ~/code/blog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+532-14081 notactuallytreyanastasio/blog/main Apr 24 17:34
7c11f89 Remove dead receipt printer modules that reference undefined ReceiptPrinter dependencies Delete three files that alias/call ReceiptPrinter.ReceiptBuilder, ReceiptPrinter.NetworkClient, and ReceiptPrinter.ImageProcessor -- modules that don't exist in this project. None of these files are referenced from routes, supervision trees, or any other code: - lib/blog/receipt_printer_service.ex (Blog.ReceiptPrinterService) - lib/blog/receipt_printer/message_handler.ex (Blog.ReceiptPrinter.MessageHandler) - lib/receipt_printer_emulator.ex (ReceiptPrinterEmulator) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0d371f1 fix compiler warnings: remove unused functions, fix function headers - Remove unused functions: message_visible?/2 (allowed_chats_live), scale_position/3, require Logger (pong_god_live), generate_id/0 (bluesky_hose), render_mobile_view/1, render_mobile_modal/1, render_desktop_view/1, post_expanded?/2, get_demo_categories/1, filter_demos/2 (post_live/index), unused alias Types (play_live) - Add function header for pick_weighted_choice/2 (story_demo) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7d57770 remove 28 dead LiveView files with no routes or references Deleted unrouted LiveViews verified via grep to have zero references outside their own files: - ai_dev_live, article_live, bezier_triangles_live, editor_live - generative_art_live, mta_bus_live (superseded by mta_bus_map_live) - muenster_live (already commented out), museum_live (admin exists separately) - rainbow_live, vim_tweets_live, war_live - jetstream_skeets_live (no route, receipt_printer.py caller) - All nathan_* variant views (ascii, buzzfeed, comparison, content_farm, harpers, teen_vogue, usenet) - none referenced from nathan_live.ex Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
893c628 fix: remove dead route references and fix undefined module warnings - Remove 11 dead route path references from PostLive.Index demos list (/war, /bezier-triangles, /generative-art, /gay_chaos, /nathan_harpers, /nathan_teen_vogue, /nathan_buzzfeed, /nathan_usenet, /nathan_content_farm, /nathan_comparison, /nathan_ascii) - Remove CairnController API routes from router (module does not exist) - Replace EarmarkParser.as_html!/2 calls with MDEx.to_html!/1 in MarkdownEditorLive (EarmarkParser.as_html! is undefined) - Remove unused alias EarmarkParser from EnhancedMediaComponent Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60bdb01 move receipt printer Python scripts to priv/scripts/ Relocated receipt_printer.py and receipt_printer_service.py from project root to priv/scripts/ for proper organization. Updated System.cmd calls in emoji_skeets_live.ex and jetstream_comparison_live.ex to use Application.app_dir/2 for release-compatible path resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91c4262 fix: remove unreachable handle_format/7 clauses that matched on has_selection=true The has_selection variable is always hardcoded to false in handle_event ("insert_format", ...), making all 12 clauses that pattern-match on true in the 7th position dead code. Removed those clauses and simplified the remaining ones to use _has_selection since only the no-selection path is currently reachable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
58c634c fix compiler warnings: unused requires, aliases, variables, pin operators - Remove unused `require Logger` from terminal_live, python_demo_live, reddit_links_live, phish_component, image_processor, skeet_channel, poke_around.migrate - Remove unused `alias Scenario` from designer_logic - Prefix unused `id` variables with underscore in poke_around.migrate - Add pin operators to bitstring size() in simple_png.ex - Remove unused `require Logger` from poke_around.migrate Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d2b49fd infra: commit Caddy static-site blocks and container mounts Sync repo with the deployed Caddyfile and docker-compose.yml. These additions were made live (either on the server or locally without committing) and rsynced out, but never tracked in git. That drift is exactly what took blimp.bobbby.online down on 2026-04-05 -- the prior blimp block existed only on the server, and the next deploy overwrote it with the older committed version. Caddyfile: add blocks for deciduous.dev, donny, blimp, phishjamexplorer. docker-compose.yml: add /opt/deciduous-site, /opt/phstats, /opt/donny, /opt/blimp volume mounts on the caddy service. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
958fdcd clean up Application.ex: remove duplicated supervisor tree - Removed rescue block that duplicated the entire children list (was missing HoseMonitor and LiveDraft compared to the main list) - ETS creation already handles existing tables safely, so the rescue for ArgumentError was unnecessary - Removed war_players ETS table (war_live was deleted) - Kept Pythonx env vars (used by PythonDemoLive) - Removed verbose inline comments Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f07a2e2 Fix compiler warnings across seven files - terminal_live.ex: Group handle_info/2 and handle_event/3 clauses together, move format_time/0 out of callback grouping - blackjack_live.ex: Move process_game_info/5 out of handle_info/2 clause group, remove unused default arg from render_player_hand/4 - python_demo_live.ex: Group handle_event/3 "reset" clause with other handle_event clauses - keylogger_live.ex: Fix head_tags component by moving modal attrs (id, inner_block) to before modal/1 in core_components.ex - map_live.ex: Remove unreachable catch-all clauses for parse_spotify_link_to_embed_url/1 and parse_apple_music_link_to_embed_url/1 - aside_component.ex: Add missing @impl true before render/1 - seed_tags.ex: Fix incompatible types in Tags.tag_link/3 call Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5218c3c add cairn stuff for annotations on the web
dc7cf3f pointless
4e0427c Fix compiler warnings in PostLive.Index - Add @impl true before mount/3, handle_info/2, handle_event/3, render/1 - Group all handle_event/3 clauses together (move modal/category handlers up) - Unwrap {:ok, msg} tuple from Chat.save_message before accessing .id - Remove dead {:error, _} branches from add_banned_word and check_for_banned_words (both functions always return {:ok, _}) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7f2ef6d remove root directory cruft: Gigalixir artifacts, scripts, Chrome extension Deleted: - Gigalixir deployment artifacts: Procfile, Procfile.bak, elixir_buildpack.config, phoenix_static_buildpack.config - Random scripts: bach-brandenburg-electronic.js, my_song.js, chat_client.py, migrate.exs, test_printer.exs, write_post.sh - Generated artifacts: output.html, package-lock.json, erl_crash.dump - Receipt printer local artifacts: com.bobbby.receipt-printer.plist, receipt-printer.service, printed_receipts/, logs/ - tasty_chrome/ Chrome extension (belongs in its own repo) - files (empty tracked file) Kept: receipt_printer.py and receipt_printer_service.py (actively called by emoji_skeets_live, jetstream_comparison_live via System.cmd) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
41fa6ee remove situation blocks and elixirc_options that break compilation Removed situation_command and situation_verbose from mix.exs elixirc_options - these Elixir 1.20 experimental features were causing compilation failures (LLM timeout, missing compiler option errors). Replaced three situation blocks in terminal_live.ex: - Visitor IP logging intent -> TODO comment - Online user marquee intent -> TODO comment - Connected check -> simple if/do block Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+14-3 bglusman/zeroclawed/feat/security-profiles Apr 10 14:24
6c3199d ci: align job names with required status check contexts Ruleset requires contexts named fmt-and-clippy, test, and build, but the workflow was reporting them as 'Format & Clippy', 'Test Suite (...)', and 'Release Build'. Drop the display names so jobs report under their keys, and add a 'test' aggregator that fans in from the matrix so a single 'test' context reports.
+700-204 notactuallytreyanastasio/deciduous/main Apr 08 11:28
c06a5da chore: update deciduous tooling to v0.14.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+188-51 notactuallytreyanastasio/deciduous/main Apr 08 11:26
236c8c5 release: v0.14.0 - observation title + description support Observations now have first-class title + description semantics: - CLI warns when creating observations without -d - `deciduous nodes` shows description inline for observations - Web viewer detail panel shows description prominently below title - All templates, commands, and instructions updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+49-12 notactuallytreyanastasio/elixir/add-hole-operator Apr 07 01:04
f24f307 Rewrite system prompt with detailed Expert context guidance The system prompt now explains: - Every context section Claude receives and how to use it - That callers tell you the expected return shape - That sibling modules show available APIs — use them, don't invent - That functions must actually exist (no hallucinating APIs) - To write inline logic when needed functionality doesn't exist - Session continuity across multiple holes in one compile - That generated code gets written to source for developer review This directly addresses Claude hallucinating nonexistent functions like Blog.Visitors.known_ip?/1 by explicitly instructing it to only call functions visible in the Expert context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+63-17 notactuallytreyanastasio/elixir/add-hole-operator Apr 07 00:55
2bc621b Add automatic retry on compile errors with error feedback to Claude When generated code fails to expand/compile, the error is captured and sent back to Claude: "The previous code had this compile error: [error]. The code that failed: [code]. Please fix it." Uses the existing session continuity (--continue) so Claude has full context from the original intent + its previous attempt. Retries up to situation_max_retries times (default: 3) before giving up. Output shows the attempt number and error at each retry: │ Compile error (attempt 1/4): │ undefined function foo/1 │ Asking Claude to fix... Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+20-1 notactuallytreyanastasio/elixir/add-hole-operator Apr 07 00:52
af3498c Strip markdown fences from Claude responses before parsing Claude sometimes wraps code in ```elixir ... ``` fences despite system prompt instructions. The parser now strips these before attempting to parse the response as Elixir code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+17-4 notactuallytreyanastasio/elixir/add-hole-operator Apr 06 23:58
6996fc6 Filter Kernel and SpecialForms from situation prompt imports These are auto-imported in every module and just add noise to the prompt. Claude already knows about Kernel functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+32-1 notactuallytreyanastasio/elixir/add-hole-operator Apr 06 23:45
a63639d Add situation_verbose for debug logging of prompts and responses When situation_verbose: true is set, the compiler prints the full system prompt, user prompt (with all Expert context), command details, and raw Claude response for each hole. Off by default. Set via mix.exs: elixirc_options: [situation_command: "claude", situation_verbose: true] Or at runtime: Code.put_compiler_option(:situation_verbose, true) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+120-38 notactuallytreyanastasio/elixir/add-hole-operator Apr 06 23:15
ce1f489 Enrich Expert LS context and add Claude session continuity Expert context enrichment — when Expert LS engine is running, each hole now receives: - Callers of the current function (what callers expect) - Function definition/spec (the stated contract) - Module attributes (@moduledoc, @type, etc) - Struct definitions in the module - Sibling modules in the same namespace - Related ExUnit tests All queries use safe_expert_call which returns [] on failure, so compilation never breaks if Expert isn't running. Claude session continuity — the first hole in a compile run starts a fresh Claude session. Subsequent holes use --continue to resume the same session, so Claude naturally accumulates context from previously filled holes. One Claude "brain" across the entire compilation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+15719-180 notactuallytreyanastasio/blimp/main Mar 30 18:59
54bff04 fix: slice(s, start, len) uses length not end index -- 10/10 lexer tests pass The slice builtin treated arg 3 as end index but the lexer (and the docs) use it as length. Fixed to start+len semantics. This was the root cause of all 4 failing lexer tests -- keyword matching, newlines, comments, and actor definitions all used slice to extract substrings. Also: nil-safe comparisons in evaluator, nil-comparable in checker, char_code returns nil for nil input. The self-hosted Blimp lexer (370 lines) now passes all 10 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2c7cbee feat: canvas output pane on every lesson Every lesson shows an Output canvas that displays the running system's result: values for expressions, rendered views for view code, errors in red, print output in blue. Canvas always visible with "Hit Run to see output" placeholder. Consistent Run button across all lesson types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f9ac5f7 feat: autocomplete shows automatically as you type Completions appear after 2+ characters, updating on every keystroke. No need to hit Tab first. Exact matches are suppressed. Menu stays open while typing and updates live. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a8f8dd9 deploy: Blimp Chat live at http://5.161.181.91:8080 Cross-compiled Blimp binary for Linux x86_64, deployed to Hetzner. Running as systemd service (blimp-chat.service), 780KB memory. Port 8080 opened on firewall. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0d5030f fix: and/or as expression operators, pipes with map/filter/reduce Bug 1: 'and' and 'or' now work as expression operators alongside && and ||. Added kw_and/kw_or to token table and parser. x > 0 and x < 10 # works now Bug 2: Pipes with higher-order functions (map/filter/reduce/each) now work. Pipe evaluator routes through evalFuncCall instead of only checking the builtin registry. [1,2,3] |> filter(_, fn(x: Int) do x > 1 end) # works now [1,2,3] |> map(_, fn(x: Int) do x * 10 end) # works now Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5e6441d Merge branch 'worktree-agent-a29a62a7' # Conflicts: # chunks/lang/web/server.blimp
b6a40a0 feat: BOOTSTRAP -- Blimp compiles Blimp end-to-end, 56/56 tests The self-hosted compiler compiles programs with: functions with return type annotations, && and || boolean operators, case expressions, nested function calls, list operations, string builtins, comparisons, and multi-line programs. Fixed: self-hosted lexer now tokenizes &&. Self-hosted parser handles or/and/|| /&& in the precedence chain. Self-hosted evaluator executes && and ||. Parser case-branch scoping fixed (assignments in case branches don't leak to outer scope). Self-hosted Blimp: 2186 lines across 5 components. Zig bootstrap: 19101 lines. Compression ratio: 8.7x. 429 lib/lexer.blimp 752 lib/parser.blimp 479 lib/eval.blimp 69 lib/complete.blimp 457 lib/tests (lexer+parser+eval+bootstrap+complete) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
949a13a feat: WebSocket broadcast chat -- zero Any types, typed actors Full rewrite with proper types: - ChatRoom, Sessions, Connections, HTTPServer all actor-typed - No Any types anywhere -- every param is ChatRoom, Sessions, etc. - WebSocket: upgrade handshake, ws_loop, broadcast to all clients - Connections actor tracks open WS file descriptors - broadcast() pushes rendered HTML to ALL connected clients - JSON builder with proper escaping (no string concat hacks) - Client JS: WebSocket with reconnect, form intercept, DOM update WS upgrade confirmed working (101 + state push). HTTP login + cookie confirmed working (302 + Set-Cookie). Remaining: render_messages crashes on map lookup from actor state (likely a scoping issue with lookup on maps from become). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bc6b4fb fix: nil-safe comparisons, type checker allows nil in ==/</> char_code returns nil for nil input instead of crashing. Comparisons (< > <= >=) return false when either side is nil. Type checker treats nil as comparable with any type. Lexer nil guards in is_digit, is_alpha, is_alnum, scan functions. Lexer tests: 6/10 pass (multiline tests need further debugging in the type checker's interaction with case-match bodies). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3841a17 feat: auto-scheduling -- no manual schedule() needed <-- auto-drains mailbox immediately after enqueue. Reduction counter also triggers drain every 4000 evals. No explicit schedule() call required. w <-- :compute(5) # enqueued AND processed print(w <- :get) # 25 -- already done Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fcd258c fix: remove Google Fonts import that blocks page rendering
b85fa45 test: 75 property tests across the entire compiler -- 7,500 random cases Properties cover: - Arithmetic: commutativity, associativity, identity, zero, negation - Comparison: complementarity, reflexivity, negation - Boolean: identity, absorption, De Morgan's laws - Lists: reverse/reverse, sort idempotent, append length, cons, sum linearity, uniq idempotent, range length, elem/head - Strings: concat identity/length, slice identity, char roundtrip, to_string/to_int roundtrip, upcase/downcase - Maps: put/lookup, overwrite, keys/values length, merge identity - Higher-order: map length, filter length, filter complement, map identity, reduce=sum, map compose, filter idempotent - Self-hosted compiler: lexer tokenizes random ints, self-eval of random arithmetic (add/sub/mul), comparisons, variables - Generators: range bounds, length bounds, type correctness - Actors: cumulative state, reset, spawn independence Total suite: 698 tests (623 unit + 75 property) = 15,123 test cases (623 + 75*100). All green in 5.6 seconds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12b9018 feat: WASM codegen -- Blimp compiles to WebAssembly The self-hosted compiler now has a WASM backend (lib/codegen.blimp, 225 lines). Compiles Blimp integer expressions to valid .wasm modules that run in any WASM runtime. compile_to_wasm("2 + 3") -> [0,97,115,109,...] (40 bytes) # Node.js: main() returns 5 # Browser: main() returns 5 Supported: integer literals, +, -, *, /, ==, !=, <, >, <=, >=, &&, ||, unary negation, booleans, nested expressions. Output: valid WASM module exporting "main" -> i32. Bytecode: i32.const, i32.add/sub/mul/div_s, i32.eq/ne/lt_s/gt_s/ le_s/ge_s, i32.and/or. New builtins: write_bytes(path, byte_list), read_file(path). 23 tests (18 unit + 5 property/500 random compilations). All green. The full pipeline: Blimp source -> self-hosted lexer -> self-hosted parser -> self-hosted codegen -> valid .wasm bytes -> runs in browser. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e62b8d4 fix: strip quotes from WASM string results in validators WASM eval returns strings as "hello" (with quotes) but validators compared against hello (without). Added unquote() helper. Fixed Greeter lesson validator. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fdc1689 feat: --self-hosted flag -- Zig bootstraps, Blimp runs ./blimp file.blimp --self-hosted The Zig runtime loads lib/*.blimp (lexer, parser, evaluator, codegen, completion, compiler), then executes user code through the self-hosted pipeline. The Zig lexer/parser/evaluator become the bootstrap layer only. ./blimp expr.blimp --self-hosted # eval via Blimp compiler ./blimp expr.blimp # eval via Zig compiler (default) New builtins: write_bytes(path, bytes), read_file(path). compiler.blimp provides blimp_compile(source, :eval/:wasm/:tokens/:ast). The Zig is now the kernel: builtins + WASM bridge + CLI. Everything above it is Blimp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3b7a55b feat: rewrite tutorial -- views by lesson 6, live preview pane 20 lessons restructured: values (3) -> variables/functions (2) -> views (5, with live rendering!) -> actors (3) -> map/filter/pipes (3) -> putting it all together (4). View lessons show rendered output in a light preview pane. Tab inserts spaces, Cmd+Enter submits, progress saved to localStorage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fca8a7c feat: completion engine in Blimp -- replaces complete.zig logic 69 lines of Blimp replaces the completion matching logic. Keywords and builtin names derived from lists, prefix matching via starts_with. 8/8 tests pass, 52/52 total. Self-hosted Blimp: 1674 lines (lexer 429 + parser 699 + evaluator 477 + completion 69). Tests: 432 lines, 52 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6ba6da7 fix: Functions lesson no longer loops -- merged into one check The 'then' hint showed 'Now try: double(21)' but the next eval matched the same lesson's check (double still in env), creating an infinite loop. Merged into one lesson that checks for double(21) == 42. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
26adbc0 feat: <-- operator for async message sends actor <- :msg is sync (blocks until reply) actor <-- :msg is async (enqueues in mailbox, returns :queued) New token: async_send (<--), parsed as MessageSend with is_async. Lexer checks 3-char <-- before 2-char <-. Evaluator enqueues in mailbox instead of executing inline. schedule(N) drains mailboxes. w <-- :compute(5) # queued, returns :queued schedule(100) # processes all queued messages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e1a14b2 feat: THE canvas -- generative hex actors, message rays, REPL blob Added the real BlimpCanvas from web/canvas.js to the tutorial. Actors render as generative-filled hexagons with SHA-hashed patterns. Message sends shoot animated rays from the REPL blob with trailing heads and impact rings. Variables show as squares. State changes flash white. Auto-layout in hex grid. This is the compile-time artifact canvas that's always been part of the Blimp REPL -- now in the tutorial. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6924581 fix: add debug output for WASM load failures
612f934 feat: WASM-powered interactive Blimp tutorial at /tutorial/ 14 lessons running entirely client-side via blimp.wasm (130KB). Predict output, fill blanks, write code -- all evaluated in-browser. Curriculum: values, variables, functions, actors, builtins, pipes. Dark theme, mobile-friendly, progress bar. Deploy: rsync docs/ to server, lives at blimp.bobbby.online/tutorial/ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3f4844c fix: rebuild WASM with all new builtins, gate native-only code Native-only functions (TCP, fork, WS, file IO) are stubbed on WASM via comptime dispatch. Hole operator returns nil on WASM. Assert builtins skip stderr on WASM. WASM binary: 178KB (was 131KB). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
65988cd feat: tutorial IS the REPL -- guided lessons in the REPL stream Complete redesign: the tutorial is the REPL itself with lesson guidance. Lessons appear as styled text in the REPL output, you type at the blimp> prompt, results show inline, state sidebar updates live, views render in the view pane. 18 lessons: values -> strings -> atoms -> booleans -> variables -> lists -> functions -> views -> actors -> messages -> map/filter -> free play. /lessons restarts, /skip advances. Same REPL as index.html: history, multi-line (shift+enter), view rendering, state sidebar, mounted actor views. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
50afcf7 feat: multi-actor view composition with mount() Actors own their state via become, render via :render handler. mount() sends :render + wraps in data-actor boundary for routing. JS DOM walk finds nearest actor boundary on button click. New builtins: mount_root, actor_name, to_atom Demo: Counter + Greeter composed in one page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
01ccc98 feat: bootstrap loop closed -- 44/44 tests, Blimp compiles Blimp blimp_self_eval("source") runs the full self-hosted pipeline: source string -> Blimp lexer -> Blimp parser -> Blimp evaluator -> result 12 bootstrap tests: integers, arithmetic with precedence, strings, variables, function def/call, nested calls, builtins, comparisons, case expressions, list ops, multi-line programs. All pass. Full suite: lexer (10) + parser (11) + evaluator (11) + bootstrap (12) = 44 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bfeabd0 test: 623 tests -- comprehensive coverage of the entire language Builtins: 231 tests -- every string/list/map/math/logic/HOF builtin with edge cases (empty, nil, zero, out-of-bounds) Operators: 142 tests -- all arithmetic, comparison, logical, pipe, send, concat operators with type combinations Language: 125 tests -- actors, become, guards, case, for, def, fn, closures, cons lists, maps, interpolation, scoping, dot notation, actor interaction, edge cases Lexer: 36 tests -- all token types and edge cases Parser: 34 tests -- all AST node types Evaluator: 31 tests -- all eval paths Completion: 8 tests Bootstrap: 16 tests -- full self-hosted pipeline 4014 lines of tests. 1.5 seconds to run. All green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e0396f2 feat: ExUnit-inspired test runner for Blimp New syntax: test "name" do ... end inside actor bodies. Suite actor state fields are re-evaluated per test for isolation (spawn Counter gives each test a fresh instance). Assertions: assert, assert_eq, assert_ne, refute with colored left/right output on failure. Two run modes: ./blimp file.blimp --test (single file) ./blimp test test/ (discover *_test.blimp files) Green dots for pass, red F for fail, failure summary, timing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b48d91d fix: reuse REPL artifact for canvas -- same styles, same rendering Output tab now mirrors the REPL exactly: - REPL output lines (=> result, prints in blue, errors in red) - View pane with blimp-* CSS classes from index.html - State pane showing vars (green names) and actors (with fields) - Message log (arrows with target + message) No custom SVG. Same artifact as the compiled REPL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
396f630 fix: enforce type annotations on ALL parameters, no exceptions Lambda params now require types: fn(x: Int) not fn(x). Checker traverses all function call arguments to catch lambdas passed to map/filter/reduce/each. Complete audit: on :msg(x: Int) -- enforced (existing) def foo(x: Int) -- enforced (previous commit) state x: Int :: 0 -- enforced (existing) fn(x: Int) do end -- enforced (this commit) The only untyped bindings are assignment (x = 42, inferred) and for loops (for x in list, inferred from iterable). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8b2911d ship: rebuild WASM (182KB), update tutorial + manifesto WASM rebuilt with all fixes: and/or operators, pipes with filter/map/reduce, Any type checks, <-- async sends, auto-scheduler, cons operator, self-hosted compiler. Manifesto pipe example now uses filter |> map |> sort |> reverse. Tutorial reference pane adds and/or, <-, <-- operators. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
19c0f35 feat: reference cheat sheet pane next to state sidebar Right column has State/Reference tabs. Reference pane covers the full stdlib: math, strings, lists, higher-order (map/filter/ reduce), maps, views, actors, control flow, utility. Compact format with green function names and gray descriptions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d1a3c48 feat: Blimp stdlib -- 185 lines replacing Zig builtins Pure Blimp implementations of: - Math: abs, max, min - Lists: sum, range, flat, uniq, list_contains, zip, set_at - Strings: upcase, downcase (char-by-char transformation) - Views: text, heading, bold, italic, code_inline, divider, stack_view, row_view (as pure map constructors) empty?, nil?, not stay as Zig primitives (can't be self-referential). 51 tests (44 unit + 7 property). All green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3bbdd09 feat: real system canvas showing actors, state, messages, views Output tab now shows the full running system: - Actor boxes (purple name, green state fields) - Message log (arrows with target + message name) - Rendered views in a light preview pane - Return value - Variable pills All pulled from WASM getState() which exposes the full actor registry, state fields, and message log. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16017bb feat: self-hosted Blimp compiler -- 32/32 tests pass Fixed three bugs: 1. Cons operator [h | tail] dropped the tail (evalList ignored the tail field). Now correctly concatenates head + tail. 2. Any-typed params couldn't send messages (checker rejected them as non-actor types). Fixed resolveActorName to accept Any/hole. 3. Test runner first-pass errors now logged instead of silent. The self-hosted compiler (1608 lines of pure Blimp) passes all 32 tests: lexer (10), parser (11), evaluator (11). Blimp can tokenize, parse, and evaluate itself. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
99b1feb feat: cookie-based session identity for Blimp HTTP server Pure Blimp string parsing — no Zig changes. Each browser gets its own session via Set-Cookie: blimp_session=<token>; HttpOnly; SameSite=Lax. Token from random()+now(), Sessions actor stores per-session state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c51bbe3 feat: Tab autocomplete in the tutorial REPL Tab triggers completion dropdown from blimp.complete(prefix). Shows function names with signatures, variables, actors, builtins. Arrow keys navigate, Enter/Tab accepts, Escape dismisses. Dropdown positioned above the input, scrollable, styled to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9c6c3a0 feat: WebSocket working in production -- graceful dead fd handling Fixed: ws_write_frame and tcp_write return :error instead of crashing on dead file descriptors. broadcast_fds auto-removes dead connections. Server no longer crashes when browsers disconnect. WS confirmed working on Linux production server: - Upgrade handshake (101 Switching Protocols) - Initial state push (JSON with rendered messages + users) - Dead fd cleanup on broadcast - Server stays active after WS disconnect Live at http://5.161.181.91:8080 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4c80350 feat: Blimp Chat -- first real web app, fully working Multi-user chat room with: - ChatRoom actor: messages, users, join/leave/send - Cookie-based sessions with auto-generated tokens - Login page, chat page with sidebar (online users) + message list - Message send via POST, redirect-after-POST pattern - Dark theme, mobile-friendly, auto-refresh every 3s - Multiple simultaneous users see each other's messages Fixed: sequential become statements (only last takes effect), slice_list using elem() instead of head/tail in case branches, Any-typed actor ref passing through function chains. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6261950 fix: add type annotations to function params in tutorial lessons Blimp requires typed parameters: def double(n: Int), not def double(n). Fixed lessons 5 (double) and 18 (profile_card). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4ea148c fix: Any type accepted in runtime type checks checkType("Any") now returns true for all values. Previously def foo(a: Any) followed by a <- :msg crashed because the runtime type checker compared ActorRef.type_name with "Any" and found no match. All 4 bugs from the audit are now fixed: 1. and/or as expression operators -- fixed 2. Pipes with map/filter/reduce -- fixed 3. Type checker scope leak -- fixed (was the and/or bug) 4. Any-typed params in evaluator -- fixed (this commit) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d2b1898 test: comprehensive coverage -- 125/125 tests pass Lexer: 36 tests (was 10) -- all tokens, operators, delimiters, keywords, comments, line/col tracking, edge cases Parser: 34 tests (was 11) -- all literals, all operators, unary, parens, func calls, defs with return types, actors, handlers, dotted names, message sends, lists, case, spawn, multi-statement, empty input Evaluator: 31 tests (was 11) -- all literals including nil/bool/atom, all arithmetic, all comparisons, && and ||, variables, function def/call/nested, builtins, lists, case with wildcard and no-match, spawn Completion: 8 tests Bootstrap: 16 tests -- full pipeline source->lex->parse->eval Also fixed: lexer handles underscore via char code (95) to avoid conflict with Blimp's wildcard pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c77779e feat: SVG system canvas with actor nodes, message rays, state Output tab now renders a live SVG diagram: - Actor nodes as purple-bordered rounded rects with state fields - Message rays (yellow animated lines) from REPL to target actors - "you" node as message origin - Ray pulse animation on each message send - Variables as pills, print output, rendered views below the graph - Canvas paint happens before validate to avoid state clobber Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
eaa72de feat: Code/Output tabs on every lesson Two tabs: Code (editor + Run button) and Output (canvas with rendered result). Run auto-switches to Output tab. Tap back to Code to edit. Visible on every lesson type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
785a030 feat: self-hosted parser in Blimp -- 11/11 tests pass Recursive descent parser (699 lines) that produces map-based AST: - Actor definitions with dotted names, state, handlers, tests - Function definitions with typed params and return types - Expressions: arithmetic, comparisons, function calls, message sends, list literals, case expressions, spawn, assignment - Operator precedence: comparison > addition > multiplication Combined with lexer (429 lines): 1128 lines of pure Blimp that can tokenize and parse Blimp source code into an AST. Also fixed: string literal lexing uses char code 34 instead of escaped quote in case branch, scan_string uses char codes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7b23f3a feat: visual actor canvas with boxes, state fields, and message rays Output tab now shows a real visual diagram: - Actor nodes as purple-bordered boxes with state fields in green - Message rays as animated dashed yellow lines from "you" to actors - :message labels on each ray - Staggered ray animation (0.2s delay per message) - Diagram auto-layouts actors in a row with SVG overlay - State text and view pane below the diagram Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d364115 Merge branch 'worktree-agent-a34cbc3d' # Conflicts: # chunks/lang/src/builtins.zig # chunks/lang/web/server.blimp
a7c6b52 fix: actor definition no longer auto-spawns actor Foo do ... end registers the template but does NOT spawn. First message send (Foo <- :msg) auto-spawns the singleton and replaces the atom binding with an actor_ref. spawn Foo creates additional instances. Canvas only shows actors that are actually alive, not templates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9526197 feat: property-based testing with generators New syntax inside actors: property "name" do given x: gen_integer(0, 100) given xs: gen_list(10, 0, 50) assert_eq(reverse(reverse(xs)), xs) end Each property runs 100 times with fresh random inputs. Generators: gen_integer(min, max) random int in range gen_string(max_len) random printable ASCII gen_boolean() random true/false gen_list(max_len) random list of ints gen_list(max_len, lo, hi) random list with range gen_one_of([a, b, c]) random element from list Parser: property/given keywords, PropertyDef/GivenStmt AST nodes Evaluator: property runner (100 iterations), given binds generated value in scope per iteration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0001b87 fix: chat deploys cleanly -- WS fallback, no server crashes - WS upgrade returns 400 (disabled for now, Linux WS builtins need debugging separately) - JS only attempts WS connection on chat page, not login page - Auto-refresh fallback every 2s when WS unavailable - Server no longer crashes on WS upgrade attempts Tested on production: login, session, chat, message send all work. Server stays up across multiple requests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
63dac0f feat: actor scheduler -- mailbox, reduction counter, async sends The scheduler infrastructure: - Mailbox: per-actor FIFO message queue (mailbox.zig) - Scheduler: run queue with round-robin processing (scheduler.zig) - Reduction counter: every eval() costs 1 reduction (4M default) - ActorEntry gets mailbox + reductions fields New builtins: - send_async(actor, :msg, args...) -- enqueue without blocking - schedule(ticks) -- process queued messages in round-robin The scheduler enables concurrent actor execution: send_async(w1, :compute, 5) send_async(w2, :compute, 10) schedule(100) # both workers process their messages Sync sends (<-) still work as before for backward compat. All existing tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fc29d3c fix: pipes lesson and predict validator Pipes with higher-order fns (filter/map) broken in pipe evaluator. Changed lesson to sort/reverse/length which work. Predict lessons no longer show Correct when the eval itself errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
17e3527 feat: interactive Blimp tutorial with runtime eval/test blimp_eval(code) and blimp_test(code) builtins for runtime code evaluation. 12-lesson tutorial: values, functions, actors, builtins. Three step types: predict output, fill blanks, write code. Dark theme, mobile-friendly (viewport meta, 44px tap targets, responsive breakpoint at 480px). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ad0a059 fix: autocomplete now includes all view primitives + new builtins Added to completion engine: stack, row, grid, text, heading, bold, italic, code, code_block, blockquote, divider, list, link, image, button, input, textarea, select, form, mount_root, char_at, char_code, from_char_code, set_at, to_atom, actor_name, assert, assert_eq, assert_ne, refute, blimp_eval, blimp_test. Added keywords: test, and, or, not. 'sta' now shows both 'state' and 'stack(children...) -> ViewNode'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c884d57 feat: WebSocket chat -- no more page reloads The chat room now uses WebSockets for real-time updates: - Initial page load is HTTP (serves HTML + WS client JS) - Client opens WebSocket to /ws - Messages sent as JSON frames over WS - Server pushes rendered HTML updates instantly - Client replaces DOM content without page reload - Exponential backoff reconnect on disconnect Server-side: accept_loop detects Upgrade header, does WS handshake (SHA-1 + Base64), enters ws_chat_loop that reads frames and pushes state. JSON escaping for safe embedding. No more setTimeout(reload, 3000). Instant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ba0e10f feat: Blimp lexer written in Blimp -- first self-hosting step Added char_at, char_code, from_char_code builtins for char-level string scanning. The lexer (lib/lexer.blimp) tokenizes: integers, floats, strings, atoms, identifiers, upper identifiers, keywords, all operators (+, -, *, /, ==, !=, <-, |>, ::, .., ..., ->), comments, and whitespace. 6/10 tests pass (multiline and keyword matching need the string case-match fix). 370 lines of pure Blimp. The compiler is starting to eat itself. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7bebcdc fix: autocomplete derives from builtin registry, not hardcoded list Completions now iterate eval.builtins.entries.items directly. New builtins automatically appear in autocomplete without maintaining a parallel list. Higher-order builtins (map/filter/ reduce/each) added separately since they're in evalFuncCall not the registry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1f7478c feat: the Blimp manifesto -- full language vision with live code Long-form page covering every premise: values, functions, pipes, actors, become, message passing, dot notation supervision, views as message responses, the web framework in Blimp, the Hole operator, the test runner, runtime internals (arenas, scheduling, LLVM), WASM in the browser, the canvas, and the self-hosting dream. Every code example has a Run button that evals in WASM. Actor examples have the canvas showing hexagons and message rays. Written in the voice: casual, direct, confident about taste. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ef9b0c2 feat: self-hosted evaluator in Blimp -- 10/11 tests pass Tree-walking evaluator (480 lines) that executes map-based AST: - Literals: int, float, string, bool, nil, atom - Arithmetic: +, -, *, / with correct precedence - Comparisons: ==, !=, <, >, <=, >= - Variables: assignment and lookup via Env actor - Functions: def, call, params, closures over env - Builtins: 25+ delegated to host (print, concat, length, etc.) - Lists: literal construction and operations - Case expressions: pattern matching with wildcard - Actors: definition, spawn, message send (1 test remaining) Combined self-hosted compiler: lexer (429) + parser (699) + evaluator (480) = 1608 lines of pure Blimp. Also fixed: checker allows Any-typed vars as message send targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
57205b0 fix: form falls back to HTTP POST when WebSocket not connected e.preventDefault() only fires when WS is ready. Otherwise the form submits normally via POST. Messages always send now. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c57724f fix: chat room fully working -- the 'bug' was a corrupted binary The Zig evaluator is fine. The elem()/head()/tail() calls all work correctly at any call depth. The crash was caused by a corrupted binary from sed-based debug print removal that left the builtins.zig in an inconsistent state. Clean rebuild fixes everything. The full chat flow works: - Login (POST /login, cookie, session) - Chat rendering (messages from actor state via head/tail) - Message sending (POST /send) - Multi-user (Bob sees Alice's messages) - WebSocket upgrade + broadcast Used head/tail recursion instead of elem/idx for rendering (simpler and avoids the index-based patterns). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0b96330 fix: no auto-spawn on message send -- must explicitly spawn actor Foo do ... end registers a template. Foo <- :msg is now an error: "Message send target is not an actor." You must: f = spawn Foo f <- :msg Fixed manifesto examples, dot_notation example. All tests pass. This is the correct semantic: define is not spawn. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
da4afa2 feat: forms, websockets, concurrency builtins + string match fix Three parallel agents' work merged as one (worktree isolation failed): - Forms: input/textarea/select/option/form view primitives, list flattening in makeViewNode, todo list demo replacing counter - WebSockets: ws_accept_key (SHA-1+Base64), ws_read_frame, ws_write_frame, view_diff tree diffing, client JS with fallback - Concurrency: fork/waitpid/exit, tcp_set_nonblocking/tcp_poll, poll-based concurrent_server.blimp demo - Bug fix: string pattern matching in case expressions (lexer stores "POST" with quotes, runtime values are unquoted) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
09b06e5 feat: secure string composition -- SafeHtml, SafeSql, context automaton Implements the accumulator pattern from the paper: - SafeHtmlAccumulator: tracks HTML parse context through fixed parts, applies context-sensitive escaping for each interpolation - SafeSqlBuilder: parameterizes user input ($1, $2...), never interpolates - HTML context automaton: pcdata/tag/attr_name/attr_value/url states - Context-sensitive escapers: html_escape, html_attr_escape, url_escape - Escaper selection based on current parse context (href -> url_escape) - Reified safe types: %{__safe_html__: true, content: "..."} 22/34 tests pass. Security-critical tests all pass: - XSS prevention: <script> tags escaped in pcdata - URL injection: javascript: URLs escaped in href attributes - SQL injection: user input parameterized, never interpolated - Safe text passes through unescaped Context automaton tests have a scoping issue (12 failures). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ca21d7e feat: dot notation for actor names in message sends and spawn Shop.Checkout <- :add(10) now parses, type-checks, and evaluates. Parser accepts upper_identifier after dot, eval/checker flatten DotAccess chains into dotted name strings for registry lookup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ca61a07 feat: real-time broadcast -- all WS clients get updates instantly When any user sends a message or joins/leaves, ALL connected WebSocket clients receive the updated state immediately. Architecture: - HTTP handles login/send/page loads (blocking, one at a time) - WS upgrade registers the fd in Connections actor, keeps it open - After any state change (join/send/leave), broadcast() pushes rendered HTML to every registered WS fd - Client JS replaces DOM content on WS message The accept loop handles both HTTP and WS upgrade requests. WS fds stay open after upgrade for push-only communication. Messages go via HTTP POST form, updates arrive via WS push. Open two browser tabs, type in one, see it in the other instantly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bfe8565 fix: reject untyped function parameters at compile time def double(n) do ... end is now a type error: "function parameter 'n' is missing a type annotation" Must be: def double(n: Int) do ... end Checker now validates def_stmt params the same way it validates handler params. WASM rebuilt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13 pushes github.com/notactuallytreyanastasio
phangraphs — Phish 3.0 Jam Analytics
phangraphs
Soul Planet
.458
BATTING AVG
11 JC · 24× played
Avg 14.0m · Peak 46:49 · 🎧 18/24
Longest: 46:49
2021-08-31 · Shoreline Amphitheatre
Most Loved: 30 likes
2021-08-31 · Shoreline Amphitheatre
📊 75% Set 2 · 9/11 JCs from Set 2
Last: 2025-07-12 (287d ago) · avg every 111d
Best year: 2021 — 4/4 JC
🔥 JC Streak: 3 in a row
"Breaks out with some brief funk and quickly evolves into a warmer groove with Pa..."
TAP TO SEE AND PLAY JAMS
Soul Planet .458
TAP TO FLIP BACK
11 jams
2018-07-28 16:08
The Forum · Inglewood, CA
Breaks out with some brief funk and quickly evolves into a warmer groove with Page and Trey trading lead duties before guitar takes over as the jam gallops to a peak and dissolves into "Wingsuit".
2018-08-04 23:40
Verizon Wireless Amphitheatre at Encore Park · Alpharetta, GA
Pivots out of the verses into a grimy jam that briefly picks up speed before devolving into envelope-filtered-Mike-led funk, then blossoms into a warm and relaxed jam full of delicate synths from Page and sympathetic Trey soloing. Things take an anthemic turn at Mike's urging, then appear to be coming to a close, but then gathers up energy once more and builds to a driving peak. But rather than close out, Fish and Page lead the way to another filthy and funky groove, with tinges of 2.0 jamming style, before dying out and > into "ASIHTOS".
2018-11-02 16:56
MGM Grand Garden Arena · Las Vegas, NV
Soaring, anthemic play from Trey combined with gooey bass from Mike highlight this second set opening version. After a mini-peak, the jam eventually fizzles to > "Down With Disease".
2021-08-10 13:27
Hersheypark Stadium · Hershey, PA
The other Summer 2021 "Soul Planet", this bad boy doesn't take long to move into a quicksilver minor-key boil with Trey's gnarly effects taking center stage. Fish picks up the pace and a solid groove emerges, then Trey switches to major-key and the band follows with a powerful bit of hose. Fish then switches up his beat and Page takes center stage, before Trey elbows his way in with some wicked riffing and Fish steers things to more rock-oriented waters. Trey then fires up some stabbing chords before neatly -> into NICU. Inspired, melodious jamming.
2021-08-31 46:49
Shoreline Amphitheatre · Mountain View, CA
Smoothly runs the gamut from dark grooves to a lovely uplifting space to double-time rocking out to a muscular jamming zone where Mike's drill (!) takes center stage to anthemic bliss to something akin to an alien distress signal that could've only come from Summer 2021 to sludgy industrial noise to even faster rocking out to snappy funkiness to a perfect -> into "The Final Hurrah". A cornerstone jam of Summer 2021, and the third longest of their entire career. Yes, you read that right, their entire career.
2021-10-22 18:19
Ak-Chin Pavilion · Phoenix, AZ
"Hmm... this jam is okay, I can imagine many of my friends lapping this up but I'm mostly disintere-SWEET HEAVENLY LORD THIS FINAL PEAK IS AMAZING."
2021-10-31 14:34
MGM Grand Garden Arena · Las Vegas, NV
Deep in the third set of the Halloween show, the band still has enough creative juice for one more killer jam, as Fish kicks up the tempo and Page does some fine electric piano work, before a slower and more contemplative jam emerges. Fish picks the pace back up and the band enters anthemic mode, Trey's funky new filter adding extra spice, then things get darker and nastier almost out of nowhere with a stomping and powerful jam. -> into "Death Don't Hurt Very Long" out of the muck.
2022-08-05 14:51
Atlantic City Beach · Atlantic City, NJ
> from "Axilla (Part II)". Mike's bass is thumping as the jam begins. Trey solos over a cool texture provided by Mike and Fish. A slow tempo gradually picks up steam, possibly because of Fish dropping a "Yeah" sample seemingly out of nowhere. The jam comes to a head around 13:00 with Trey and Mike both heavy in their effects arsenals. It melts into almost nothing before > into a big "Down With Disease".
2023-07-22 14:11
The Pavilion at Star Lake · Burgettstown, PA
Hard-charging out of the gates through 7:15 when it settles into an ethereal transition, then winds up again for a heartening resolution and floats into -> "Twist".
2024-08-04 26:13
Ruoff Music Center · Noblesville, IN
Along with preceding "Ghost", this "Soul Planet" makes up one of the great 1-2 punches of the year as it sets off at a good pace and introduces dial-tone and other wonky effects in the opening minutes before a shift around 8:00. From there it drifts upwards into major bliss and is driven into gorgeously inspirational territory by Trey's coalescing riff for a terrific mid-jam hosing, after which the band briefly gestures towards a return home, only to quickly launch into more locked-in improvisation. With the vibe now turning towards hard-rock, the jam once again gels around Trey's riffage and surges to a head-banging peak, then > "Billy Breathes".
2025-07-12 12:34
North Charleston Coliseum · North Charleston, SC
There's no time wasted in this all killer, no filler jam, which gets especially gnarly after 8:00 when it develops a mean streak and goes screaming through space to a supernova conclusion.
phangraphs Phish 3.0 Jam Analytics
AIM Chat - Terminal
Online (0)
jeff 02:52 AM
What is up y'all
jeff 03:44 AM
Thanks for visiting my website
jeff 03:51 AM
Trying from mobile let’s see what’s up
Uechi Nerd 03:54 AM
Hi, greetings from Planet Crackpot!
jeff 03:55 AM
oh man what's up
jeff 03:56 AM
Hey everyone
Uechi Nerd 03:56 AM
winding down with a beer or three
Uechi Nerd 03:57 AM
desktop version
Uechi Nerd 04:01 AM
Disappointed that War does not lead to actual combined-arms conflict.
jeff 04:01 AM
that would be hard to conjure
jeff 04:02 AM
I am so excited that this works and is a successful combination of windows and old apple lol
Uechi Nerd 04:02 AM
Probably for the best, actually. That shit is very very messy.
Uechi Nerd 04:02 AM
I am intrigued and happy it works!
Uechi Nerd 04:03 AM
I respect the wizardry.
Visitor7804 04:05 AM
this is delightful.
jeff 04:06 AM
hell yeah visitor 7804, this is livin' brother
guy4get 04:07 AM
i've never felt so alive
EarlofVincent 04:09 AM
Commencing experiment in 3....2....
jeff 04:14 AM
1
leah 04:16 AM
hi!
leah 04:16 AM
this is lovely
jeff 04:21 AM
hi! lol I was just like what if I combined Mac and windows and added a flower tree of life and called it my homepage and then smoked some weed and made it happen in an empty mall in Connecticut
B. Droptables 10:51 AM
Always cool to play with your toys.
Visitor1128 08:47 AM
yo!
Visitor1128 08:48 AM
i can barely work my phone. what am i doing here?
jeff 09:04 AM
the phone is not optimized yet but it "kind of works" I am sorry lol
jeff 09:04 AM
you have to pick a username, then it goes to the chat, then if you hit the bottom tabs it'll let you go to the app sections.
Bobdawg 04:43 AM
Hi everybody this is my blog I hope you enjoy it I did some more changes and anyone can write a post here now for me.
dinkleberg 01:45 AM
ALL HAIL TREE OF LIFE
jeff 08:55 PM
hi Hacker News
jeff 04:28 PM
hey there I am not really Jeff
Mal Function 05:34 PM
Hey! Please reveal... how exactly do I actually use losselot on my Mac? I've run the git clone commend in Terminal.app and seem successfully to have installed into a new <losselot> sub-folder in my home folder but now???