Skip to content

feat: turn birds into prompt-driven Flight School - #45

Draft
theycallmeswift wants to merge 11 commits into
mainfrom
claude/birds-flight-school
Draft

theycallmeswift wants to merge 11 commits into
mainfrom
claude/birds-flight-school

Conversation

@theycallmeswift

@theycallmeswift theycallmeswift commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

BirdsBirdBirds becomes Flight School. The attendee describes the flight in their own words and directs revisions by waypoint number, all through Pi. The Chrome page is a display-only sky on the gallery's 1000×850 stage: it draws each flight as a numbered path, ghosts the previous one, and flies the bird among the booth flock. Every flight flies in from off screen and out again. When they're happy, Pi publishes without asking and says it can take the bird down; it does so with withdraw_gallery_item if they ask.

  • Flight history: the page server copies each new passing bird.json to .flights/NNN.json, using a debounced watch plus a check on every page poll. It serves the latest two as stage-pixel waypoints at /flight.json, with the waypoints that changed: those no previous waypoint sits on, whatever their number, so inserting a loop leaves the renumbered tail plain. The waypoints come from check-bird.mjs's own parser, which now returns what it read.
  • Sky: the gallery's bird size, 3× slowdown, and 2.3 s stagger. The path draws on as straight dotted segments with round joins, waypoints pop in, then the bird flies. Revisions show a ghost path, dashed old markers, and highlighted changed segments. "Draw again" is gone; Pi reopens the canvas at /?draw.
  • Legible birds: the drawing page crops bird.png to its ink plus 6% padding before saving. The sky crops every PNG it shows the same way, cached per URL, so birds published untrimmed fill their box too. The participant's bird flies at 1.6× flock size with a white halo, above the path and markers; the flock dims to 55% opacity with slight desaturation.
  • Durations: duration_s, in the feed and in /flight.json, is the bird's own stated duration, unslowed. The page applies the 3× slowdown at playback and staggers flock birds within the slowed flight.
  • Flock: /flock.json proxies <gallery url>/birds.json and its PNGs same-origin, with a TMPDIR cache, reading BOOTHSTRAP_GALLERY_URL and then BIRDSBIRDBIRDS_GALLERY_URL. Every transform and easing must pass the checker's grammar and is written out again. No URL, offline, an error, or a 404 gives a bundled practice flock of 7 SVG birds.
  • Off-screen ends: the checker rejects a flight whose first or last waypoint leaves any of the bird box, turned and scaled, on the stage, and the skill's example and bounds now start at -20vw and end at 120vw. The sky pins the markers of those off-screen waypoints just inside the edge, where the path comes into view. The skill aims for 6–10 s flights.
  • Preview: node tools/seed-birds-workspace.mjs <dir> seeds a sample drawing and two flights, then prints the command that serves the page showing a revision.
  • Skill and manifest: Pi writes waypoint numbers as plain digits, since the station font lacks circled ones. The steps are draw-bird, animate ("Teach it to fly"), direct-flight, and join-flock; the animate hint and the sky's caption both ask for the bird's name, origin, and flight. The manifest sets skip_dialog: ["gallery"], and the skill offers the takedown in chat and calls withdraw_gallery_item.

Decide

  • /flight.json flights carry bird_name, origin, and timing_function beyond the agreed {waypoints, duration_s, version}, and waypoints may carry easing. Recommend: keep them, because the name chip needs the labels and faithful playback needs per-keyframe easing.
  • When should the pinned @mlhacks/boothstrap be bumped? Recommend: in this PR once a boothstrap release includes MLH/boothstrap.sh#688 and MLH/boothstrap.sh#690 (withdraw_gallery_item), and don't move any event's challenge ref to this change before stations run that release, because stations reject unknown skip_dialog names, which would hide the challenge. The pinned 0.0.2 ignores skip_dialog, so npm run verify passes today; boothstrap dev --verify from a #690 build passes too.

Ready gate

Step Evidence
Tests and lint On 0de450f with Node 26.10 on macOS: npm run verify passed all 4 packages, npm test passed 342 of 342, and npm run lint found 0 issues in 18 files. ✓
Red run Each new behaviour has new tests: flight history, waypoint math, motion helpers, drawing crop, flock proxy against a loopback stub, and the seed script. The insertion test fails on the old index-based diff, the unslowed-duration change flips the /flight.json duration expectation from 36 to 12, and the new off-screen tests fail without the checker rule. ✓
Sibling sweep Searched for /bird.json, Draw again, "fly", Give it a flight, Watch it fly, and circled digits ①–⑳ across the repo: none remain outside checker messages. Searched for notice, taken_down, 86vw, and 12s in the package: none remain. ✓
Docs and specs Package README (page, flight history, flock, preview, steps, the off-screen rule, and takedown through Pi), root README row, intro.md, and the skill. ✓
Standalone run Earlier head: played live in a macOS UTM guest with Gemini through to Take it down (found the circled-digit and renumbering-diff bugs, both fixed). On 0de450f, checked in Chrome with a real event drawing: the caption, a flight entering from off screen, and markers 1 and 6 pinned on the path at the edges. The takedown through Pi still needs a live run on a #690 build.
Editorial pass Reread every changed file against AGENTS.md and docs/style/. Spelled out single-letter names (n, x, y locals and iterators), documented the flock's methods and inline helpers, folded one-use helpers into the transform matrix code, and brought the test file's header up to date. ✓
Skipped tests None. ✓
Secrets, permissions The page server now makes outbound GETs to the configured gallery origin only, with no redirects, 3 s timeouts, and size caps. No secrets. ✓

The page server copies every new bird.json that passes check-bird.mjs to
.flights/NNN.json and serves the latest two as waypoints on the gallery's
1000x850 stage at /flight.json, with the waypoints a revision changed.
The sky renders on the gallery's 1000x850 stage with its bird size,
slowdown, and stagger. Each new flight draws its path on, pops in numbered
waypoints, then flies; a revision ghosts the previous path and highlights
what changed. The Draw again button is gone: Pi reopens the canvas at /?draw.
The page server proxies the gallery's birds.json and bird PNGs same-origin,
caching them in TMPDIR, and falls back to a bundled practice flock when no
gallery URL is set or the gallery can't give a valid feed.
Pi asks how the bird should fly in the participant's own words, writes at
least five in-bounds waypoints, narrates and revises them by number, pushes
back on flights that won't fit, and publishes the bird once the participant
is happy. The steps become draw-bird, animate, direct-flight, and
join-flock.
skip_dialog lists gallery, so publish_gallery_item shares straight away,
and gallery.notice_title names the notice that offers the takedown. Both
need a boothstrap release newer than the one pinned here.
…back

The boothstrap feed sends each bird's own unslowed duration, so /flight.json
does too, and the page applies the gallery's 3x slowdown when it plays a
flight. Adds tools/seed-birds-workspace.mjs, which seeds a workspace with a
sample drawing and two flights for previewing a revision.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0a50f66c-e7dc-45d8-8845-2eb2cc45c9ef
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

The drawing page crops bird.png to its ink plus 6% padding before saving,
and the sky crops every PNG it shows the same way, so birds published
untrimmed fill their box too. The participant's bird flies 1.6x flock size
with a white halo above the path, and the flock dims to 55% opacity.
The station terminal's font has no circled digits, so they rendered as empty
circles. The sky keeps drawing its own numbered markers.
… it does

Inserting waypoints renumbers the ones after them, so comparing by number
flagged the whole unchanged tail. The sky's dashed old markers now show the
previous waypoints that no current waypoint sits on.
Spells out single-letter names, documents the flock's methods and inline
helpers, and folds one-use helpers into the transform matrix code.
@theycallmeswift
theycallmeswift marked this pull request as ready for review November 3, 2026 21:37
Flights now start and end off screen, which the checker enforces, and Pi defaults to quicker flights. The perch caption matches the console step. Sharing no longer shows a notice: Pi says the bird can come down and calls withdraw_gallery_item when asked.
@theycallmeswift
theycallmeswift marked this pull request as draft November 4, 2026 16:07
@testdriverai

testdriverai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Warning

1 of 1 test run failed — see "What I ran" below before trusting any clean verdict.

This pull request updates BirdsBirdBirds into a prompt-driven "Flight School" challenge where participants describe and iteratively refine bird flight trajectories with Pi using numbered waypoints. The sky page becomes a display-only 1000×850 stage that animates trajectory paths, ghosts previous flights, highlights changed segments, crops drawings to ink bounds, and proxies booth flock birds.

All 340 existing unit/integration tests pass cleanly across packages (npm test and npm run verify). The local application was seeded with two sequential flights and driven via browser preview. The implementation is well-architected and clean, with one edge-case finding where erasing a drawing can leave anti-aliased sub-threshold pixels that cause cropOf to return undefined and throw a TypeError in canvasToPng.

📋 Walkthrough (20 files)
File Change
README.md Updates the challenge directory description for BirdsBirdBirds to prompt-driven flight school.
challenges/birdsbirdbirds/README.md Documents the Flight School architecture, display-only sky stage, flight history in .flights/, flock proxy, and preview seeding.
challenges/birdsbirdbirds/challenge.json Updates challenge metadata, steps (draw-bird, animate, direct-flight, join-flock), and adds gallery skip_dialog.
challenges/birdsbirdbirds/check-bird.mjs Extends tokenizer/parser to return keyframe AST and enforce off-screen flight entrance and exit rules.
challenges/birdsbirdbirds/flight-history.mjs Implements debounced workspace file watching and records passing flights sequentially into .flights/.
challenges/birdsbirdbirds/flight-path.mjs Converts CSS keyframes to stage waypoints, computes changed waypoints across revisions, and checks bounds.
challenges/birdsbirdbirds/flock.mjs Fetches and proxies booth flock feeds and PNGs same-origin with caching, falling back to practice flock.
challenges/birdsbirdbirds/flock/flock.json Bundles practice flock feed containing sample flights and timings.
challenges/birdsbirdbirds/intro.md Updates participant onboarding narrative to introduce Flight School and directing flights via Pi.
challenges/birdsbirdbirds/page/app.mjs Updates page flow to crop saved drawings, switch to sky mode, and poll /flight.json and /flock.json.
challenges/birdsbirdbirds/page/flight-motion.mjs Provides motion helpers for SVG path generation, distance shares, keyframes, and 3x playback slowdown.
challenges/birdsbirdbirds/page/index.html Adds sky stage markup, SVG path overlays, masks, name chip, and flock container.
challenges/birdsbirdbirds/page/sky.mjs Drives display-only sky, drawing on paths, popping in waypoint markers, ghosting revisions, and flying birds.
challenges/birdsbirdbirds/page/style.css Styles the stage, waypoints, ghost trajectories, highlight segments, and flock opacity.
challenges/birdsbirdbirds/page/trim.mjs Calculates pixel ink bounding box and padded crop rectangle from raw RGBA data.
challenges/birdsbirdbirds/page/trimmed-image.mjs Handles client-side canvas image cropping and caching via data URLs.
challenges/birdsbirdbirds/server.mjs Serves /flight.json, /flock.json, /flock/images/*, and manages flight history recording.
challenges/birdsbirdbirds/skills/start-challenge/SKILL.md Guides Pi agent on prompt-driven flight revisions, plain digit waypoint numbers, and takedown support.
test/birdsbirdbirds.test.mjs Adds tests for off-screen validation, flight history recording, waypoint diffing, and flock proxying.
tools/seed-birds-workspace.mjs Adds tool to seed preview workspace with sample bird drawing and revision flights.

🔍 Code review

From reading the diff — not reproduced in a run.

  • challenges/birdsbirdbirds/page/app.mjs:218 (🟡 Minor · bug): In isCanvasBlank(), a canvas is considered non-blank whenever any pixel has alpha !== 0. However, cropOf() in trim.mjs ignores pixels with alpha under INK_ALPHA (16) as anti-aliasing noise.

If a participant erases strokes using the eraser tool and leaves faint anti-aliasing fringes (where 0 < alpha < 16), isCanvasBlank() returns false, causing saveBird() to invoke canvasToPng(). In canvasToPng(), cropOf(...) returns undefined because no pixel reaches INK_ALPHA, and cropped.width = crop.width immediately throws an unhandled TypeError: Cannot read properties of undefined (reading 'width'). saveBird() catches this and displays an internal error message ("Saving failed: Cannot read properties of undefined (reading 'width'). Try again...") instead of recognizing the canvas as empty.

Checking data[alphaIndex] >= 16 in isCanvasBlank() aligns blank detection with cropOf()'s ink threshold.

Suggested change
    if (data[alphaIndex] >= 16) return false

🎬 What I ran

  • ❌ failed — renders the flight school sky with bird name chip and stage — replay

test run

Was this review useful? React with 👍 or 👎 to help TestDriver improve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant