+3450-244notactuallytreyanastasio/archive_classifier/main
May 22 17:24
989dd16
feat: play button auto-advances frames every 3 seconds
βΆ starts playback from current position, βΈ pauses.
Auto-scrolls transcript to keep active segment visible.
Loops back to start at end. Manual slider interaction stops playback.
Co-Authored-By: Alpha <alpha@alphafornow.com>
3c4ac48
chore: add timing logs to cache load and Whisper pipeline
- Cache: batch ETS insert + log load time in ms
- Whisper: log model loading time, transcription time, segment count
- All timings visible in application logs at info level
Co-Authored-By: Alpha <alpha@alphafornow.com>
ab96055
feat: cache thumbnails in Postgres + serve from ETS
- thumbnail binary column on videos table (~7KB each, ~9.5MB total)
- mix fetch_thumbnails: bulk fetch from archive.org (10 concurrent)
- ThumbnailController serves from ETS cache at /thumbnails/:id
- Falls back to archive.org redirect if not cached
- LiveView and SearchLive now use local thumbnail endpoint
- Cache-Control: 1 day on served thumbnails
Co-Authored-By: Alpha <alpha@alphafornow.com>
ed6a32c
feat: supervised transcription pipeline with bounded concurrency
- Pipeline.Supervisor: houses Task.Supervisor + TranscriptionProducer
- TranscriptionProducer: GenServer with queue, max 2 concurrent jobs
- Task.Supervisor.async_nolink for crash isolation
- Classify button enqueues via producer, not bare Task.start
- Logs: enqueue, start (with active count), complete
- Pipeline added to application supervision tree
Co-Authored-By: Alpha <alpha@alphafornow.com>
85cb7d2
feat: "Watch" button linking to archive.org for every video
Opens archive.org/details/{archive_id} in a new tab.
Available on all videos regardless of classification status.
Co-Authored-By: Alpha <alpha@alphafornow.com>
d125a60
chore: upgrade to whisper-medium, delete whisper-small cache
whisper-small had poor segment timestamps on longer videos.
whisper-medium (3GB) should produce more accurate timing.
Cleared 932MB whisper-small cache.
Co-Authored-By: Alpha <alpha@alphafornow.com>
8f48610
fix: preserve ANSI colors in bin/server via script -q
Co-Authored-By: Alpha <alpha@alphafornow.com>
ae9656c
make website work as a v1 release that has some videos transcribed and working pipes
ea491c7
feat: end-to-end transcription pipeline verified
- Fixed Whisper serving: pass file path, not binary content
- Replaced FFmpeg CLI with Xav NIF bindings (FFmpeg 7.x)
- Media.Audio module uses Xav for audio extraction
- First successful transcription: "Interview with Older People
Bowie Jagger Dancing" β 28 timestamped segments in Postgres
- Pipeline: download β extract audio β Whisper β store β cleanup
Co-Authored-By: Alpha <alpha@alphafornow.com>
99cba12
chore: disable Whisper auto-start in dev
Boot was taking 4+ minutes loading the 3GB model on every restart.
Whisper will start lazily or via twerker worker process instead.
Co-Authored-By: Alpha <alpha@alphafornow.com>
c779dc5
fix: restore start_whisper: true in dev
Co-Authored-By: Alpha <alpha@alphafornow.com>
f437370
feat: repeatable dev-to-prod DB sync script
scripts/sync-db-to-prod.sh: pg_dump β scp β pg_restore
Idempotent, prints row counts on completion.
Co-Authored-By: Alpha <alpha@alphafornow.com>
79b1315
feat: dedup consecutive identical transcript segments
Whisper loops on the same phrase for many 2-second windows.
Dedup.merge_consecutive/1 collapses runs into single segments
with the full time range (e.g., 0:46 - 1:14: "I'm sorry").
Applied at both storage time (pipeline) and display time (LiveView).
6 dedup tests. 60 tests total.
Co-Authored-By: Alpha <alpha@alphafornow.com>
155ea3e
fix: frame scrub requires click+drag instead of hover
mousemove was firing on every hover, causing the slider to follow
the cursor without clicking. Now requires mousedown first.
Fixes play button not responding (mouse events were interfering).
Co-Authored-By: Alpha <alpha@alphafornow.com>
123d633
fix: add pool_timeout and log download errors with detail
Finch connection pool may starve with concurrent downloads.
pool_timeout: 5min gives queued downloads time to acquire a connection.
Error logging now shows the full reason for failed downloads.
Co-Authored-By: Alpha <alpha@alphafornow.com>
84b92cb
feat: filmstrip timeline with hover-to-read transcript
- VideoFrame schema: stores JPEG frames at 10s intervals
- Media.Frames: FFmpeg frame extraction (TODO: migrate to Xav NIF)
- Pipeline extracts frames alongside audio transcription
- FrameController serves frames at /frames/:id (1 week cache)
- TranscriptSearchLive: horizontal filmstrip with colocated JS hook
Hover a frame β shows timestamp + transcript at that moment
- Frame extraction is non-blocking: failure doesn't abort pipeline
Co-Authored-By: Alpha <alpha@alphafornow.com>
c205871
fix: Classify button fires pipeline in background Task
- Clicking Classify now actually starts the transcription pipeline
- Runs in a background Task so LiveView stays responsive
- Flash message confirms transcription started
- Dev logs include timestamps for ML timing visibility
Co-Authored-By: Alpha <alpha@alphafornow.com>
3e74793
fix: invert text color on all children when segment is highlighted
The timestamp badge and text span had their own color styles
overriding the parent. Now sets color on all children.
Co-Authored-By: Alpha <alpha@alphafornow.com>
4ae751e
feat: admin dashboard for mass-enqueuing transcription jobs
- AdminLive at /admin with composable filters:
Duration range (MM:SS), collection, search, status
- list_videos_filtered/1 with duration range support
- enqueue_videos/1 mass-enqueues via Twerker
- Live match count updates as filters change
- Enqueue All button with count badge
- Admin link in toolbar
- Fixed test sandbox: Cache GenServer allowed in sandbox
- 6 admin tests, 66 total
Co-Authored-By: Alpha <alpha@alphafornow.com>
0a5573f
test: add 17 new tests + better pipeline logging
New tests:
- Video changeset (6): required fields, enum values, defaults
- Cache (7): all_videos, search, get, stats, reload
- Pipeline (4): hallucination filter logic (non-ASCII ratio, repeated words)
Pipeline now logs [pipeline] prefix at each step:
Starting, Downloading, Extracting audio, Sending to Whisper, Done
54 tests total, zero warnings.
Co-Authored-By: Alpha <alpha@alphafornow.com>
03d5b46
fix: filter Whisper hallucinations from transcripts
- Reject repeated non-ASCII character spam (Georgian, etc.)
- Reject single punctuation / empty segments
- Reject same-word-repeated-5-times hallucinations
- Trim whitespace from all transcript text
Note: whisper-small has poor segment timestamps on longer videos.
Consider whisper-medium or chunked audio for better accuracy.
Co-Authored-By: Alpha <alpha@alphafornow.com>
971a6a3
fix: remove invalid Req options (connect_timeout, pool_timeout)
connect_timeout and pool_timeout are not valid Req options.
This caused ALL 61 queued jobs to fail with ArgumentError.
Removed both β Req/Finch defaults are sufficient.
Co-Authored-By: Alpha <alpha@alphafornow.com>
8000919
feat: interactive video explorer with frame scrubbing
- Frame viewer: hover to scrub through frames like a gif
- Timeline slider: drag to any moment, frame + caption follow
- Caption bar: shows spoken words at current timestamp
- Transcript segments are clickable buttons β jump to that moment
- Active segment highlighted in blue
- Frame preloading for smooth scrubbing
- Colocated .VideoExplorer JS hook handles all interactions
- Dockerfile: compile before assets.deploy (colocated hooks need build artifacts)
Co-Authored-By: Alpha <alpha@alphafornow.com>
f952adb
feat: replace in-memory queue with Twerker persistent job runner
- Twerker as path dep (../twerker)
- twerker_jobs table for persistent queue (survives restarts)
- Classify/Reclassify buttons use Twerker.enqueue/3
- Removed old Pipeline.TranscriptionProducer (replaced by Twerker)
- Twerker PipelineSupervisor in app tree with 2 consumers
- Disabled in test env (start_twerker: false)
Co-Authored-By: Alpha <alpha@alphafornow.com>
01d6eb5
fix: hallucination filter was rejecting all segments
The regex [\P{Latin}\P{Common}] matched everything including English.
Replaced with non_ascii_ratio check β only reject if >50% non-ASCII.
Added logging for filtered segment count.
Co-Authored-By: Alpha <alpha@alphafornow.com>
0e4740a
feat: per-video transcript viewer + URL state on search
- TranscriptSearchLive at /videos/:id/transcript?q=<term>
Per-video drill-down: shows all segments, filter by spoken words
Timestamps, segment count, fully shareable URL
- SearchLive at /search?q=<term>
URL-driven state via handle_params + push_patch
Works from shared links β mount reads q param
- 37 tests passing, zero warnings
Co-Authored-By: Alpha <alpha@alphafornow.com>
a022d12
fix: increase download timeouts and add retry config
connect_timeout: 2 min, receive_timeout: 30 min, retry: transient, max_retries: 3.
Archive.org can be slow under concurrent requests.
Co-Authored-By: Alpha <alpha@alphafornow.com>
ced409b
perf: enable EXLA persistent cache for compiled graphs
Without this, the Whisper compute graph gets JIT-recompiled from
scratch on every server restart (~4 minutes). With cache_dir set,
compiled graphs persist to disk and reload in seconds.
Co-Authored-By: Alpha <alpha@alphafornow.com>
6c3e091
feat: add Reclassify button for classified/failed videos
Clears old transcripts and frames, resets status, re-enqueues.
Co-Authored-By: Alpha <alpha@alphafornow.com>
c886dad
feat: deploy infrastructure for archive.bobbby.online
- Dockerfile: multi-stage build with FFmpeg 7 dev libs for Xav NIF
- deploy.sh: rsync + sidecar into blog's docker-compose stack
- Auto-patches docker-compose.yml, Caddyfile, init-db.sh
- Creates archive_classifier_prod database
- Builds, deploys, migrates, reloads Caddy
- Release scripts: bin/server, bin/migrate
- Prod config: Whisper disabled by default, logger at :info
- Runs on port 4002 behind Caddy at archive.bobbby.online
Co-Authored-By: Alpha <alpha@alphafornow.com>
0bb9235
feat: live job queue viewer on admin page
- Shows all Twerker jobs sorted by status (running β queued β failed β completed)
- Color-coded rows: yellow=running, red=failed, green=completed
- Shows elapsed time, attempt count, error snippets
- Auto-refreshes every 5 seconds via handle_info timer
Co-Authored-By: Alpha <alpha@alphafornow.com>
ba4b2d8
fix: enable chunked transcription for long audio
Added chunk_num_seconds: 30 to Whisper serving config.
Without this, Bumblebee only processes the first 30 seconds
and hallucinates/repeats for the rest. Now it properly chunks
the audio and produces accurate timestamps across the full video.
Co-Authored-By: Alpha <alpha@alphafornow.com>
5b47e93
feat: bin/server wrapper that tees logs to logs/server.log
Enables unified log tailing from Alpha's bin/logs.sh
Co-Authored-By: Alpha <alpha@alphafornow.com>
53df079
feat: "Transcribed only" checkbox on catalog, checked by default
Filters to only show classified videos so users see what they
can actually search transcripts for. Uncheck to see everything.
Co-Authored-By: Alpha <alpha@alphafornow.com>
6735012
feat: exponential backoff for archive.org downloads
1s β 2s β 4s β 8s β 16s between retries, max 5 attempts.
Logs each retry with delay. Temp files confirmed cleaning up.
Co-Authored-By: Alpha <alpha@alphafornow.com>
11babf5
feat: full-text search across video metadata AND transcript content
- tsvector columns + GIN indexes on videos and transcripts tables
- Auto-maintained via Postgres triggers on insert/update
- search_videos_fts/1: returns videos matching in title, description, OR spoken words
- Cache.search now uses FTS for non-empty queries (ETS for empty)
- Searching "sphinx" finds videos where someone SAID "sphinx" even if
it's not in the title
Co-Authored-By: Alpha <alpha@alphafornow.com>
05ddf00
feat: link classified videos to transcript viewer
Co-Authored-By: Alpha <alpha@alphafornow.com>
+122-124notactuallytreyanastasio/Beta/main
May 22 13:45
+21-8notactuallytreyanastasio/twerker/main
May 22 12:28
fc1a000
feat: recover orphaned jobs on producer startup
Jobs left in "running" status from a previous crash/restart get
reset to "queued" so they're picked up again. Prevents stuck jobs.
Co-Authored-By: Alpha <alpha@alphafornow.com>
f70c1a3
fix: fetch jobs as structs instead of relying on returning: true
update_all with returning: true can return nil in some Ecto versions.
Re-fetch jobs after update to ensure full structs are dispatched.
Co-Authored-By: Alpha <alpha@alphafornow.com>
+122-8notactuallytreyanastasio/twerker/main
May 22 12:04
f3b8098
docs: README with setup, usage, architecture
Co-Authored-By: Alpha <alpha@alphafornow.com>
+1631-281notactuallytreyanastasio/archive_classifier/main
May 22 05:46
6f18d1a
fix: real HTML popover for description on hover
Dark tooltip appears below the card on hover. Only shows when
description differs from the title.
Co-Authored-By: Alpha <alpha@alphafornow.com>
2d8175c
perf: add composite index on (collection, duration) for cache load
Co-Authored-By: Alpha <alpha@alphafornow.com>
e1dd630
fix: strip HTML tags from description popovers
Archive.org descriptions contain raw HTML (<div>, <br />).
Strip to plain text before rendering in the tooltip.
Co-Authored-By: Alpha <alpha@alphafornow.com>
bae1865
feat: collection-first drill-in UI, sort dropdown, thumbnails
- Landing shows 7 collections as cards with thumbnails and total duration
- Click collection to drill in to its videos
- Sort dropdown: duration asc/desc, title asc/desc
- Thumbnails from archive.org/services/img/{archive_id}
- Auto-fill grid adapts columns to viewport width
- Stripped Phoenix default navbar and branding
- Search filters both collection view and drill-in view
Co-Authored-By: Alpha <alpha@alphafornow.com>
126bfab
fix: remove dead code caught by Elixir 1.20 type inference
The || fallback and nil clause were unreachable β the :if guard
already guarantees description is non-nil. Type inference is sharp.
Co-Authored-By: Alpha <alpha@alphafornow.com>
77a514c
refactor: integer PK, archive_id field, Ecto.Enum for status
- Video uses standard integer PK (Ecto handles it)
- archive.org identifier stored as archive_id with unique index
- classification_status is now Ecto.Enum (:pending, :queued, :classifying, :classified, :failed)
- All tests updated, 15 passing, zero warnings
Co-Authored-By: Alpha <alpha@alphafornow.com>
810dfe7
feat: transcript search page at /search
- SearchLive: search spoken words across all transcribed videos
- Results show video thumbnail, title, timestamp range, matched text
- Transcript schema tests (changeset validation, ilike search, case-insensitive)
- Route: GET /search
- CLAUDE.md: codified test-first development as mandatory
Co-Authored-By: Alpha <alpha@alphafornow.com>
32aeb19
feat: ETS cache for all videos, 3-column grid grouped by collection
- Cache GenServer loads all 1,371 videos into ETS on startup
- Search runs in-memory (catalog is static, fully digitized)
- Cache.reload/1 updates single video after classification change
- LiveView: 3-column grid, videos grouped by collection
- Collections get human-readable names (Ron Wood, Jackson Browne, etc)
- Zero Postgres queries on page load/search after startup
Co-Authored-By: Alpha <alpha@alphafornow.com>
9c8e0ac
feat: catalog LiveView with search and classify trigger
- Archive context with list_videos, count_videos, stats, queue_for_classification
- CatalogLive: browse catalog, search by title/description, trigger classification
- 11 context tests covering search, filtering, status, pagination
- Removed dead PageController (replaced by LiveView at /)
- exqlite available in all envs (no hacky warn suppression)
Co-Authored-By: Alpha <alpha@alphafornow.com>
b069fb9
feat: wire Whisper serving into supervision tree
- Conditionally starts via :start_whisper config flag
- Model configurable via :whisper_model (whisper-small in dev, whisper-tiny in test)
- Tests never load the model (start_whisper: false in test.exs)
Co-Authored-By: Alpha <alpha@alphafornow.com>
ae4e559
feat: transcription pipeline β Whisper, FFmpeg, transcript storage
- ML.Whisper: Bumblebee Nx.Serving for whisper-small with segment timestamps
- Media.FFmpeg: audio extraction (16kHz mono WAV) and duration probe
- Classification.Transcript: Ecto schema with video_id, start_time, end_time, text
- Pipeline.Transcribe: download β extract audio β transcribe β store β cleanup
- Whisper-small verified loading on M4 Pro (967MB model)
Co-Authored-By: Alpha <alpha@alphafornow.com>
71c4eaa
fix: show full titles, description on hover
Co-Authored-By: Alpha <alpha@alphafornow.com>
+6430-0notactuallytreyanastasio/Beta/main
May 22 04:48
5d6eaf9
add supporting docs and site stuff to publish
+6683-11notactuallytreyanastasio/Beta/main
May 22 04:33
0ff60ec
wire it up so that it just runs all the things, assuming you have ollama and can run the qwen model alongside the regular shit
+30-29notactuallytreyanastasio/deciduous/main
May 07 12:47
6e4f0c0
update docs RE cowork not working yet
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+30424-14135notactuallytreyanastasio/deciduous/main
May 07 02:27
a1203a4
release: v0.15.0 - MCP server for Claude Code, Desktop, and cowork
Built-in MCP server (`deciduous mcp`) exposes 31 tools over the Model
Context Protocol. Works with Claude Code, Claude Desktop, and cowork.
- Graph CRUD, querying, full-text search, chain tracing
- Session management: each conversation gets its own decision tree
- Sessions persist across server restarts for long-running cowork
- /query slash command for natural language decision graph reports
- Full docs at deciduous.dev/mcp with API reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+500-1notactuallytreyanastasio/blimp/main
May 01 11:26
b7e10a5
Merge tutorial/ch07-clock: Chapter 7 ("The Clock")
A logical Clock actor (callers advance via :tick) plus a
BikeShare.Rider extended with pickup_at and an :elapsed handler.
The chapter argues for logical clocks over now() for both
testability and WASM compatibility (now() in the WASM build is
stubbed to 0). Solution passes 7/7 in CLI and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91f7dfd
feat(tutorial): chapter 7 -- The Clock
Adds 07-clock.md and the ch07_clock exercise. A simple Clock
actor with one Int field that callers advance via :tick. The
BikeShare.Rider from Chapter 6 grows pickup_at and a guarded
:elapsed handler that reports how long the rider has had the bike.
The chapter explains why the clock is logical (callers send :tick)
rather than wall-clock: WASM's now() returns 0, and even a working
wall clock would make tests flake. Pattern is the same one used
by Erlang/OTP and most distributed systems for testable time.
Three TODOs:
- Clock's :tick (the canonical "increment counter, reply new value"
shape from earlier chapters)
- Rider's guarded :pickup, now threaded with a Clock arg, capturing
the pickup time into pickup_at via a multi-field become whose
right-hand side is a `clock <- :now` send
- Rider's guarded :elapsed, computing (clock <- :now) - pickup_at
Solution passes 7/7 in CLI and WASM. Stub Exercise hints are
prose-only; no commented-out answer code under any TODO.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+26-35notactuallytreyanastasio/blimp/main
May 01 04:29
11006e2
Merge stubs/no-answer-comments: stubs no longer ship the answer
ch4/5/6 exercise stubs had Exercise hints with the literal
solution lines commented out underneath, which defeated the prose
rewrite from the previous merge. Hints are now prose-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ee19920
docs(exercises): replace commented-out answers with prose hints
The TODO comments in ch4/ch5/ch6 stubs had the answer code
commented out underneath, which is exactly the copy-paste shape
the prose rewrite was meant to remove. Each Exercise: hint is
now a prose description of what the handler should do, with no
literal lines the reader can uncomment.
Stubs still parse, solutions still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+492-1notactuallytreyanastasio/blimp/main
May 01 01:45
3ce6a62
feat(tutorial): chapter 6 -- The Rider
Adds 06-rider.md and the ch06_rider exercise. Introduces a
BikeShare.Rider actor that holds one user's session: name and a
Bike slot that's nil when idle.
New language ground:
- nil as a meaningful state value
- == nil and != nil guards on multi-clause handlers
- the pre-flight check pattern (send to downstream, dispatch on
the reply, only commit your own state if downstream confirmed)
Three TODO handlers; the multi-clause :pickup and :return get
their fallback clauses pre-wired so the reader's work is the
guarded "do the thing" clauses. The rider also lives at
BikeShare.Rider so each instance is its own supervision boundary
under the same prefix as Vault and Inventory from chapter 5.
Solution passes 7/7 in CLI and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6b1ee2b
Merge tutorial/ch06-rider: Chapter 6 ("The Rider")
A BikeShare.Rider actor with a Bike-or-nil session slot. New ground
is nil as a state value, == nil / != nil guards, and the pre-flight
check pattern (ask the bike before committing the rider's own
state). Each rider lives at BikeShare.Rider so it inherits the
supervision boundary from chapter 5. Solution passes 7/7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
+199-243notactuallytreyanastasio/blimp/main
May 01 01:11
0373af8
Merge tutorial/parallel-examples: ch3-5 steps no longer copy-paste
Each "Step N: fill in X" section now shows the language pieces as
small parallel patterns and asks the reader to compose the actual
handler, rather than leading with the exact answer in a code block.
"Putting it together" recap blocks removed; standalone concept
sections that overlapped with the new step content folded into the
steps themselves.
No code changes; solutions still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7987591
docs(tutorial): rewrite ch3-5 step sections with parallel examples
The "Step N: fill in X" sections used to lead with the exact answer
in a code block, then walk through it line by line. That made the
exercises a copy-paste rather than a synthesis, and the "Putting it
together" recap blocks doubled the answer count.
New shape: each step shows the language pieces in small parallel
patterns (a Counter that takes a typed arg, a Stack that pops the
head, an integer-list reduce, a divide-by-zero bubbler) and asks
the reader to compose the actual handler from those parts. The
exercise file's solution stays the canonical assembled answer.
Also folded the standalone "Sending messages from inside a handler"
section in ch3 and "Sending into a closure" section in ch4 into the
steps that needed the concept, since they were duplicating ground
the steps now cover with their own parallel snippets.
No code changes; solutions still pass 8/7/5 in CLI and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b138476
Merge tutorial/ch05-supervision: Chapter 5 ("The supervision tree")
Introduces bubble / orelse / bubbles(CascadeBubble) and the dotted-name
supervision tree. Exercise wires a BikeShare parent to a Vault and an
Inventory; the vault bubbles on overdraw, the parent translates the
bubble into a tagged tuple, and an :emergency handler cascades a reset
across every BikeShare.* sibling at once. Solution passes 5/5 in CLI
and WASM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ea72016
feat(tutorial): chapter 5 -- The supervision tree
Adds 05-supervision.md and the ch05_supervision exercise.
Introduces three new pieces of language ground:
- bubble :reason raises a failure that propagates as a value
- target <- :msg orelse X catches a bubble at the call site
- bubbles(CascadeBubble) handler annotation: when this handler bubbles,
restart every dotted-name sibling under the
same supervisor prefix
The exercise builds three cooperating actors: a BikeShare.Vault that
holds cents, a BikeShare.Inventory that holds a bike count, and a
BikeShare parent that wires them together. Three TODOs:
- :withdraw bubbles :overdraw on insufficient funds
- :charge translates the bubble into {:error, :declined} via orelse
- :corruption is annotated bubbles(CascadeBubble) so a panic resets
every BikeShare.* sibling
Solution passes 5/5 in the CLI and in the tutorial WASM evaluator.
The chapter walks through bubble vs. tagged tuple as a design choice
(state preserved vs. erased), the dotted-name supervision boundary,
and the bulkhead pattern of unrelated supervisor prefixes never
cascading into each other.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19809f3
fix(ws): use the correct RFC 6455 magic GUID for Sec-WebSocket-Accept
The handshake constant in builtins.zig had the magic GUID's hex
digits scrambled:
258EAFA5-E914-47DA-95CA-5AB9DC80CB65 (wrong)
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 (RFC 6455 Β§1.3)
curl doesn't validate Sec-WebSocket-Accept against the locally
computed expected value, so handshakes from curl saw the 101 and
the WS frames started flowing -- the chat *looked* like it worked.
Real browsers do validate, and rejected every connection with
"Incorrect 'Sec-WebSocket-Accept' header value", which is why
Enter on the chat input never actually delivered: the WS was never
open, the form fell through to its HTTP POST fallback, and the
page reloaded.
Fixes the GUID, fixes the test that was calibrated to match the
bug (it now expects the RFC 6455 example value
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" for key "dGhlIHNhbXBsZSBub25jZQ=="),
and adds a deploy snippet to infra/README.md for rebuilding and
shipping the chat interpreter.
Verified end to end on the live box: handshake response now
matches the RFC value, and a headless-Chrome session through
https://blimp.bobbby.online/chat/ logs in, types a message, hits
Enter, and sees the message broadcast back via WebSocket without a
page reload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1949b0c
Merge chat/fix-ws-handshake: WebSocket handshake now follows RFC 6455
The ws_accept_key builtin was using a corrupted magic GUID, which
curl's loose handshake check let slide but every real browser
correctly rejected. Fixes the constant, fixes the test that was
calibrated to the bug, and documents the rebuild/redeploy steps
for the chat interpreter in infra/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9228098
chat: serve at blimp.bobbby.online/chat/ via Caddy reverse proxy
Three changes to chat.blimp so it works behind a path-stripping
proxy:
- Form actions become relative ('/login' -> 'login', '/send' ->
'send') so they resolve against whatever URL the page is at.
- Redirects use './' so the browser stays under the /chat/ prefix
instead of jumping to root.
- The WebSocket URL is now built from window.location, including
protocol (wss when the page is HTTPS) and the current pathname
prefix.
Also adds infra/Caddyfile + infra/README.md, which are the live
contents of /opt/blog/Caddyfile on the Hetzner box. The
blimp.bobbby.online block now redirects bare /chat -> /chat/ and
reverse-proxies /chat/* to 172.18.0.1:8080 (the blog_default
docker-bridge gateway, where the blimp-chat systemd service is
listening on the host's 0.0.0.0:8080).
Verified end-to-end: GET /chat/ serves the login HTML, POST
/chat/login redirects with a session cookie, GET /chat/ws does
the WebSocket upgrade and immediately receives the broadcast
frame.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
337d1f2
Merge chat/path-aware: chat lives at blimp.bobbby.online/chat/
chat.blimp uses relative URLs (form actions, WebSocket, 302
redirects) so it works behind a path-stripping reverse proxy.
Caddy on the Hetzner box now proxies /chat/* on blimp.bobbby.online
to 172.18.0.1:8080, where the blimp-chat systemd service is
listening. Adds infra/Caddyfile + infra/README.md so the proxy
config is tracked alongside the source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
01f1408
feat(tutorial): chapter 4 -- Zoom out
Adds 04-zoom-out.md and the ch04_zoom_out exercise. Introduces
anonymous functions (`fn(x: T) do ... end`) and the three list
builtins that take them as arguments: map, filter, reduce. The
chapter's exercise builds a `BikeShare` actor that holds a list of
`Station` references and fans questions (total bike count, names of
busy stations) out to all of them via map / filter / reduce.
Three TODO handlers (`:open`, `:total_bikes`, `:busy_stations`).
Solution passes 7/7 in CLI and in WASM.
The original ch4 roadmap said "supervision tree, dotted names, zoom
out". This chapter takes the "zoom out" half of that and ch5 will
take supervision + dotted names. Updated 00-why-actors.md's roadmap
to reflect the split, and updated the ch5 placeholder title in the
SPA chapter list.
Implementation note: at this zoom level Stations track bikes by an
integer counter rather than by holding [Bike] actor references. A
runtime hang shows up when actor refs are passed as typed message
args into actors that later get iterated by a parent's
closure-send; we work around it here, will revisit alongside ch5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4a74600
Merge tutorial/ch04-zoom-out: Chapter 4 ("Zoom out")
Anonymous functions, map/filter/reduce, and a `BikeShare` parent
actor that fans questions out to a list of `Station` children.
Seven tests; solution passes in CLI and WASM.
Splits the original ch4/ch5 roadmap: ch4 takes "zoom out", ch5
takes the supervision tree + dotted names + crash isolation
material, including a runtime hang to fix where actor refs passed
as typed args interact badly with closure iteration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b541da3
docs: add self-hosting narrative to README, deslop pass
Full story of the bootstrap: prerequisites, lexer, parser, evaluator,
closing the loop, bugs found, what came after. Deslopped: cut "the
language eating itself", "fell into place fast", dramatic fragment
openers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f0aee23
docs: rewrite README from scratch using decision graph
Self-hosted compiler is the lead story. Shows the actual compiler.blimp
pipeline. Fixes chat.blimp line count (706, not 23k). Adds concurrent
server, safe strings, property tests, LTO benchmarks. Drops the old
Zig-focused implementation table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e169732
docs: README reflects self-hosted compiler, Zig is bootstrap
Blimp compiles Blimp. The self-hosted compiler (lexer, parser,
evaluator, codegen, stdlib) lives in chunks/lang/lib/. The Zig code
is the bootstrap that gets it off the ground.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5f4e961
docs: fix README accuracy -- real line counts, remove stale screenshots
Zig line counts were from months ago. Updated to actual wc -l output.
Clarified C is just the 846-line native runtime, everything else is Zig.
Removed outdated playground and REPL screenshots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5986e8a
docs: rewrite README with current project state
Update examples, line counts, WASM size, add tutorial and tree-sitter
links, restructure around key ideas rather than syntax reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9ff6564
docs(tutorial): backtick actor / type names in ch1-3 prose
Sweeps the tutorial chapters so identifiers (Bike, DockingStation,
String, status, id) render monospaced wherever they appear in prose,
not just inside fenced code blocks. Headings get the same treatment
("The shape of a Bike" -> "The shape of a `Bike`"). Code blocks and
already-backticked spans untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ab2b6bb
feat(tutorial): chapter 3 -- Actors talking to actors
Adds 03-actors-talking.md and the ch03_actors exercise (stub +
solution). Introduces a DockingStation actor that holds a list of
Bike references and orchestrates rentals: list-typed state, the
cons / head / tail / length pattern, typed message arguments
((b: Bike)), and sending messages to other actors from inside a
handler. Exercise has 8 tests; the solution passes all 8 in the CLI.
Wires ch03 into the SPA's chapter list so the tutorial picks up the
new markdown, stub, and solution files automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4a5caa8
feat(tutorial): replace textarea editor with Monaco
Drops Monaco (v0.46.0 from jsdelivr) into docs/tutorial/index.html in
place of the layered textarea + <pre> highlight overlay. Registers a
`blimp` Monarch grammar mirroring blimp-highlight.js (same keywords,
constants, builtins, atoms, #{} interpolation, PascalCase types) and a
`blimp-dark` theme matching the existing token colors so highlighting
stays visually consistent with the static code blocks rendered in the
middle pane.
Cmd/Ctrl+Enter is now a Monaco command bound to Run Tests. The cheat
tab and rendered markdown blocks keep using blimp-highlight.js (small,
sync, no behavioral change). Boot does Promise.all([blimp.init,
initMonaco]) before loading the first chapter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
39c1a3c
Merge tutorial/ch03-stations: Monaco editor + Chapter 3
Two coherent changes shipped together:
1. Replace the textarea editor in the tutorial SPA with Monaco
(v0.46.0), with a Blimp Monarch grammar and a dark theme that
mirrors blimp-highlight.js. Cmd/Ctrl+Enter runs tests via a
Monaco command. Cheat tab and rendered-markdown code blocks
keep using blimp-highlight.js.
2. Chapter 3 -- "Actors talking to actors". Introduces the
DockingStation actor that holds a list of Bike references and
orchestrates rentals. New language ground: list-typed state
([Bike]), cons / head / tail / length, typed message arguments
((b: Bike)), and sending messages from inside a handler.
Includes 03-actors-talking.md plus stub + 8/8-passing solution
under exercises/ch03_actors/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
9d20b71
Merge lang/ws-bidirectional: WASM test runner, bike share tutorial, SPA, deploy fix
# Conflicts:
# deploy.sh
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>
"Debut. Trey and Page's new "Gunn" inflected banger goes big in its live debut. F..."
TAP TO SEE AND PLAY JAMS
Life Saving Gun
.438
TAP TO FLIP BACK
7 jams
β
2023-07-30
12:56
Madison Square Garden Β· New York, NY
Debut. Trey and Page's new "Gunn" inflected banger goes big in its live debut. Focused and inventive play moves with purpose through several layers of varying texture, weaving a tapestry of sound that is a true stand out among tour highlights. Breaks > for "No Men".
β
2023-12-30
13:15
Madison Square Garden Β· New York, NY
> from "It's Ice". The song's first set debut and it fits the role of set-closer like a glove. The jam is a raucous, high-energy affair. It peaks, keeps on going and then peaks again.
β
2024-04-18
17:23
Sphere Β· Las Vegas, NV
Retains the song's promise as a set centerpiece. Bending, but not quite breaking new ground, percussive, celebratory play evokes, elementally, "Crosseyed" and "Paug," respectively, hewing that feel-good sound from a score seemingly built to spill into euphoria.
β
2025-06-20
19:07
SNHU Arena Β· Manchester, NH
After two Set 2 openers for the price of one ("Bouncing" followed by "Sample"), the band gets down to business with the longest "LSG" to date. They take a minute to find their footing, but Mike takes the reins around 9:30 and things take off. Excellent, melodic play from Trey at 14:50 gives way to a full band onslaught. The jam winds its way to its ending and > into "Twist".
β
2025-07-09
19:09
Jerome Schottenstein Center Β· Columbus, OH
The jam finds itself immediately brimming with ideas simply by using the inertia of its driving rhythmic push. A downshift around the 10 minute mark finds the band in new territories, centered around a hypnotic, repeating bass line. With an unrelenting rhythmic center, an inspired, densely layered slab of music emerges and makes itself at home, before the clarion call of "Percussion Rinse!" signals an end. Excellent.
β
2025-07-26
13:29
Broadview Stage at SPAC Β· Saratoga Springs, NY
> from "Light". A breakdown jam begins before the song proper has really ended, which gives way to some effects-laden jamming. The jam stays up-tempo and energetic throughout, before slowing down and then -> into "Waste".
β
2025-09-13
17:59
Coca-Cola Amphitheater Β· Birmingham, AL
In the midst of a stunning second set, we enter the "LSG" jam like a comet ripped apart by gravity, falling inwards towards the sun, before the band fully unifies around a Krautrock beat and driving chord progression from Trey. Fishman works against the established groove to expand the jam into a more spacious affair which allows Trey to move outside the initial ideas, pushing the band outwards once more. Listen for Page to move from the Rhodes to his synth around 9:15 which reinforces the expansive playing and kicks off a segment defined by a wall of effects and interwoven anti-melodies, reminiscent of the 10/10/23 "Ruby Waves." Chaos starts to dissipate around 16min as a clearing emerges in the jam. Marked by sprinkles of melody and interwoven riffs before a fade -> to "Pillow Jets," this is a clear peak moment in "Life Saving Gun's" young career as we hear what is fully possible with the budding jam vehicle.
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???