Version 0.56.1
- Internal: the Claude model picker's labels and the per-model effort lookup read the model table through one function, and the Antigravity settings use the same cached reader as Claude's. No change to the app.
Every project
What changed in each project, newest first. The entries come from each project's changelog file or GitHub releases. Projects without a written changelog show their commits, grouped by day. The site checks for new entries once a day.
196 entries from 9 projects, 152 of them numbered versions, since Jun 2026
78 entries in CHANGELOG.md since Sep 2026. The latest version is 0.56.1, from Nov 2, 2026. Full changelog · Project page
2 entries in CHANGELOG.md since Jul 2026. Full changelog · Project page
27 entries in CHANGELOG.md since Jul 2026. The latest version is 0.4.0, from Aug 2, 2026. Full changelog · Project page
There's no written changelog for Ironlog yet, so these are its commits grouped by day, 7 days since Jun 2026. Full commit log · Project page
60 entries in RELEASE-NOTES.md since Jun 2026. The latest version is 0.33.1, from Sep 25, 2026. Full changelog · Project page
There's no written changelog for Chess 2.0 yet, so these are its commits grouped by day, 9 days since Jun 2026. Full commit log · Project page
3 GitHub releases since Aug 2026. The latest version is 2.2.0, from Oct 9, 2026. Full changelog · Project page
6 entries in CHANGELOG.md since Aug 2026. Full changelog · Project page
There's no written changelog for Video Production Tracker yet, so these are its commits grouped by day, 4 days since Aug 2026. Full commit log · Project page
main or has local changes. Switched off, only the
button checks.PERF startup line to session.log with how long
every step took, and a PERF stall line whenever the window freezes for
more than 150 ms, naming the terminal output or redraw behind it. Nothing
changes on screen.e03956c Serve Ironlog from ironlog.thebarbellengineer.com2576636 Serve the game from chess.thebarbellengineer.com2e2a712 Publish the current build: fixes from the joint Claude and Luna review0b46ce7 Report the carbs imported cans lose2729775 Mark the review fixes ready to ship42d47fb Offer the import when exercise edits are all an old install hasee53431 Fix what Luna's full-diff review found800f1c1 Keep built-in holds timed after they are hidden8a015d2 Leave timed holds out of the day's rep count6ec0f67 Fix the bugs a joint Claude and Luna review found9f3fdfb Take the assets path from Vite's resolved config904f4e8 Throw away only generated files after a lane build0e6aae3 Write down the review flow for lanes and integration PRs346ca52 Write down the review flow for lanes and integration PRsf5b93a5 Publish the current build: checkbox-styled lift togglesec2e29d Fix the unstyled lift toggles on the Exercises panebd43660 Style the lift toggles as checkboxes, not browser buttonsShows the seller's country flag in the corner of every Vinted listing. Vinted never tells a buyer where a seller is, anywhere on the site.
Download vinted-country-flags-2.2.0.zip below, unpack it somewhere you will keep it, then in Chrome go to chrome://extensions, turn on Developer mode, click "Load unpacked" and pick the unpacked folder. If you already run 2.1.0, load the new folder and remove the old one.
Vinted changed what it serves on 2026-10-08, and 2.1.0 broke in three ways. Flags stopped on the catalog, the item-page lookup could name the visitor instead of the seller, and cards that hit a rate limit or scrolled past were never retried.
Grids past the first screenful now read the seller from the response the page already fetches for itself, on the homepage and on the catalog. The catalog endpoint 404s now, and asking for the grid again returns a different shuffle, so reading the page's own response is the only path that matches the cards on screen.
Rate limiting is tighter. The extension leaves a share of Vinted's bucket for the site's own calls, backs off when Vinted answers with a "Client Challenge" page instead of data, and frees a slot as soon as a card scrolls away.
Cards that cannot be read from their price fall back to a profile lookup, and a lookup that gets cancelled no longer leaves a second card blank.
The cache is cleared on upgrade. Some entries written by the old item-page lookup may have named the visitor, so they are dropped rather than trusted.
50 unit tests pass, up from 49 at 2.1.0. Parsers were checked against live vinted.ro pages: 96 of 96 cards named from the document, and 96 of 96 from the page-2 response.
GPL-3.0. Not affiliated with Vinted.
Agents > Confirm Ship finished lanes turns the question back on.
.worktreeinclude copied in (a .env), the same as when
it edited one. Before, the lane was removed and the deletion with it..worktreeinclude file in the repository names ignored local files (like
a .env) to copy into every new lane. A lane whose copy you edited is kept
when its agent closes, not deleted with it.b1f41e6 Add MIT licenseb52e8a3 Add MIT licensea5e5cb9 Add MIT licenseShows the seller's country flag in the corner of every Vinted listing. Vinted never tells a buyer where a seller is, anywhere on the site.
Download vinted-country-flags-2.1.0.zip below, unpack it somewhere you will keep it, then in Chrome go to chrome://extensions, turn on Developer mode, click "Load unpacked" and pick the unpacked folder.
The homepage gets flags. It never did, which is a gap where most visits start.
Two things were in the way. Catalog cards are numbered product-item-id-<id>--image, but every homepage card shares one testid, feed-item--image, and carries its item id only in the link over the photo, so the selector matched nothing. And nothing could have answered those cards anyway: /api/v2/catalog/items does not return the homepage feed at all, 0 of 20 overlapped on vinted.ro, and the endpoint that does serve it answers 400 to the same query replayed by anyone but Vinted's own front end.
The homepage does name every seller, though, twice over and for free. The first screenful is server rendered and the payload the page hydrates from is still sitting in the document with "user":{"id":...} on every item. Everything after it arrives on the request the page fires when the feed paginates, where the same fact is spelled user_id, and the extension reads that answer as it goes past rather than asking for its own. Together they named the seller of 70 of 70 cards on vinted.ro, 20 from the document and 50 from one pagination response, for no request at all.
Knowing the seller is not knowing the country, and 70 known sellers is 70 lookups at one a second. So the homepage spends two catalog requests it will never match a card with, purely to learn exchange rates, and then reads the country off the price like any other card. Two rather than one because the fingerprint refuses to answer until it knows the euro rate, and a single draw carried two euro sellers, one short of the three it takes to trust a rate. That answered 66 of the 70 cards, agreeing with the sellers' actual profiles on all 13 spot-checked, and left five lookups instead of seventy.
Euro markets skip those two requests. The fingerprint cannot work where every seller prices in euro, so the homepage there falls back to a lookup per card, same as its catalog pages.
None of it is per-domain. Checked on the vinted.ro, vinted.de and vinted.fr homepages: same card markup, same payload shape, every card mapped to a seller.
49 unit tests, five of them new. GPL-3.0. Not affiliated with Vinted.
Shows the seller's country flag in the corner of every Vinted listing. Vinted never tells a buyer where a seller is, anywhere on the site.
Download vinted-country-flags-2.0.0.zip below, unpack it somewhere you will keep it, then in Chrome go to chrome://extensions, turn on Developer mode, click "Load unpacked" and pick the unpacked folder.
All 27 Vinted markets, up from vinted.ro only.
The country rule is no longer hardcoded. The market table is read from /api/v2/countries at runtime, so a currency that stops pinning a country stops pinning it on its own, and a new market appears without a release. That also dropped five dead currency rows and picked up Australia.
The rate limit is measured rather than guessed. From a cold bucket exactly 30 requests to /api/v2/users succeed and the 31st gets a 429; paced at one per second, 45 in a row draw nothing. So it is a token bucket of 30 refilling at about 1/s, shared per IP across domains. That bucket now lives in a service worker, so every tab and every Vinted domain spends one budget instead of one each. Pushback honours the Retry-After the response carries.
The cache moved to the worker too. A seller resolved on vinted.fr is already known on vinted.de, because seller ids are global across markets.
/api/v2/users/{id} returns country_code: "UK" for Britain, which is not an ISO country and resolved to no flag. It now joins country_id against Vinted's own table, which also sidesteps Greece reporting as EL.
The price fingerprint's guard was one unit of buyer currency. That is a real guard in lei and none at all in forints, where every price clears a one-forint grid. It is anchored to about 0.20 EUR now and refuses to run until it knows the rate that converts it.
Prices parse in every market's number format, taken from the page language rather than assumed.
Eighteen of the 27 price in euro. Every seller there prices in the currency you see, so the free signal says nothing: 0 of 259 items sampled on vinted.fr carried the conversion data the other markets give away. Each card costs one seller lookup, so the grid fills in over about 90 seconds rather than appearing at once, viewport first, and the popup tells you how many are left. Roughly a fifth of those sellers turn out to be foreign, which is why it is worth the wait.
252 flags, covering every ISO country, since a seller can live somewhere Vinted does not operate.
44 unit tests. GPL-3.0. Not affiliated with Vinted.
npm run build inlines everything into one standalone HTML file. Save
it anywhere and double-click it, no install.61fc517 feat: initial commit for video production trackerAn audit of how the dashboard stores user accounts and site passwords, ahead of open-sourcing it. Everything below is fixed; the through-line is that the app now assumes it may be hosted on a public domain rather than sitting on one laptop.
POST /api/runs passed the request body straight through to spawn(), so any
account with tests.run could set NODE_OPTIONS=--require … (or
CYPRESS_RUN_BINARY, PATH, LD_PRELOAD) and execute arbitrary code as the
dashboard's user. Saved schedules were the same hole with a timer on it.
Requested environment is now allowlisted to the E2E_ / WP_ / TEST_ / DASHBOARD_ namespaces, and validated when a schedule is saved rather than at
3am when it fires. A run may also no longer carry its own baseUrl: only the
PR Builder, which builds its target server-side, can target an unregistered site.
data/custom-sites.json held "adminPass": "admin" in the clear. It is now
AES-256-GCM, keyed from DASHBOARD_SECRET_KEY or a generated data/secret.key
(0600), and existing files are migrated on first boot. data/ itself is locked
to the running account. Adding a site now requires a password — the old
silent default meant a site with none would send admin/admin at whatever host
its URL pointed to.
app.listen(PORT) bound every interface while the banner said localhost, over
plain HTTP with non-Secure cookies. Now 127.0.0.1 unless HOST says otherwise,
with TRUST_PROXY to make req.secure and req.ip meaningful behind a proxy
and a boot warning when the bind is not loopback.
Express percent-decodes route parameters after matching, so ..%2f..%2f reached
path.join() in the report, artifact and run-record routes. Both ids are now
validated before they touch the filesystem.
admin!.mustChangePassword is enforced rather than advisory: the account is signed in
but inert until it sets its own password. New accounts and admin resets set it.nosniff, X-Frame-Options: DENY, HSTS over
TLS. The login page's inline script moved to login.js so script-src can be
'self'. Playwright reports get a narrowly relaxed policy so they still run.SameSite=Lax..html, .svg, .xml) are served as
downloads — they are test output from sites you may not control.window.prompt(),
which paints them onto the screen.c222faf Stop trusting the peer with the board in online play36bb779 Extract a pure move reducer out of playMove's DOM-tangled CPS chain4c60e6c Add board setup editor, clock system, and resign flow1d19b79 Publish the current build: charts, records, and the label scanner48270e3 Add a personal records card to the Progress pane6f4a7c7 Fix the label scanner: the contrast stretch was eating the text0d9b691 Fix the chart defects the harness turned up, and drop the harness727d55d Add barcode lookup to the label scanner, and fix its crop geometrybf0a5b5 Remove .vite dependency cache from the repoea72ce9 Untrack .vite dependency cache7d2f403 Give the weight input room back on a phone92611c9 Update live link — the site is actually published nowe97d3d2 Add session rating, legacy data import, and Pages deployment0ab1986 Reach full feature parity with the original buildc539465 Restore the easter eggs, unified calendar and body-composition rows35ff1a8 Close the parity gap: exercise library, gear, and both calendars0d38744 Port the macro journal to the food tab5b4480e Port the plate loader to the plates tab8095c6f Rebuild as a Vite + React + TS source project; port the workout tabupdatedAt column — Room v5→v6, additive; existing sets inherit their
creation time.)safeDrawing —
also future-proofs Android 15's forced edge-to-edge and display cutouts.HttpURLConnection (no new dependency); a flaky/offline network
fails silently. The one-time "install unknown apps" grant is requested on first use.keystore.properties), so update-over-install
works reliably. BuildConfig is enabled for the version comparison.versionCode 2).INTERNET, REQUEST_INSTALL_PACKAGES; added a FileProvider
to serve the downloaded APK to the installer.Three problems surfaced running the full 11-product suite from the dashboard, plus a reporting gap. This release addresses all four.
Why: the orchestrator launched every authenticated site's Playwright process
simultaneously (Promise.all(runnable.map(runTests))). Eleven concurrent
WordPress stacks plus a swarm of headless Chromium instances exhausted machine
memory, so the OS refused new allocations and several sites died mid-startup
with PHP Fatal error: Out of memory / nginx 502 Bad Gateway (observed on
several sites). Which sites lost was down to luck under contention, so the
failures looked random.
What changed (config.js, server/orchestrator.js):
SITE_START_STAGGER_MS (default 45000 ms). Each site clears its
memory-heavy startup (browser launch + auth + first page load) before the next
begins, so peak memory stays bounded.MAX_CONCURRENT_SITES (default 0 = off) as a complementary
safeguard; a freed slot lets the next queued site launch.launchWithStagger() + cancellableDelay() replace the simultaneous
Promise.all; a whole-run cancel no longer has to wait out the stagger.Why: the biggest, longest suites should start earliest — they need the most runway and the freest memory. Previously sites ran in selection/tree order.
What changed (config.js, server/orchestrator.js):
SITE_START_PRIORITY (default apprentice, architect, ttb, quiz) is applied to
both the auth phase and the launch phase via orderTargets(), regardless of
selection order. Remaining sites keep their original relative order. Storage /
render order is untouched.Why: a site was marked "busy" for the entire run and only released at
whole-run finalize(). So a product that already failed kept blocking a new run
for that product until every other product finished — you couldn't re-run the
failed one in the meantime.
What changed (server/orchestrator.js, server/index.js, public/app.js,
public/style.css):
busySites is now a Map<site, runId> with an ownership-checked
releaseSite() — so an old run's finalize can never free a site a newer run has
already re-acquired.cancelSite() + POST /api/runs/:id/targets/:site/cancel
kills just that site's process (children are tagged with _site), marks it
cancelled, frees it, and finalizes the run only once nothing else is active
(maybeFinalize(), gated on launchComplete; finalize() made idempotent).renderSiteActions()).Why: results were only viewable per product (one Playwright report per site). There was no single view of a whole multi-product run to read, share, or export.
What changed (server/combined-report.js (new), server/index.js,
public/app.js, public/style.css):
GET /api/runs/:id/combined-report renders a self-contained
HTML page merging every product of a run into one document, built on demand
from the persisted run.json (so it works for historical runs too — no
artifact regeneration).file:line, error), a "did not
run" note for auth-failed/empty products, and a link to each product's full
Playwright report for trace/screenshot drill-down. All interpolated strings are
HTML-escaped.@media print, page-break-avoid): open it (it opens in a new
tab) and Ctrl/⌘+P → Save as PDF — no manual PDF step.config.js — SITE_START_STAGGER_MS, MAX_CONCURRENT_SITES, SITE_START_PRIORITY
server/orchestrator.js — staggered/ordered launch, per-site cancel + ownership-aware busy release, idempotent finalize, per-site child tagging, slimTarget exposes paths/grep/custom
server/combined-report.js — new; the combined-report HTML builderserver/index.js — combined-report route + per-site cancel routepublic/app.js — per-site Cancel/Re-run buttons, combined-report links (Live + History)public/style.css — .card-actions, small button + run-head link stylingOn the machine this runs on, the local site domains didn't match the
canonical names hardcoded in the committed suite config
(.playwright/sites.config.ts). To run against the real local sites, the
suite's sites.config.ts URLs were pointed at the actual local domains. This
is a machine-specific override, intentionally kept out of this dashboard
change; the long-term fix is to rename the local sites to the canonical names
and drop the override.
reading_progress (MIGRATION_4_5).windowSoftInputMode=adjustResize and the flashcard card is
responsive, so nothing clips behind the on-screen keyboard.RomajiMatchTest); device-verified on a Pixel 7,
including the live v4→v5 migration.CharacterForm
setting (selector in the Study-scope sheet). Printed keeps the typographic
font ghost you read on signs and screens; Handwritten draws the guide ghost
from the same KanjiVG strokes the demo and scorer use (a grey brush), for both
practice and the quiz. Rendering only — it never changes what is studied,
demonstrated, or scored.TextToSpeech (ja-JP) fed the raw kana glyph — no new
dependency, permission, manifest, or Room change. If the Japanese voice is not
installed the button opens the system voice-data installer; if TTS is
unavailable it greys out. Availability re-checks on resume, so a freshly
installed voice starts working without a restart. (The dense gojūon chart cells
intentionally omit the button — no room.)ねこ matches neko, ne ko, ねこ, or cat. The
query and every searched field are run through a shared searchNormalize()
(drops all whitespace, lowercases), so romaji typed with or without spaces and
in any case compare equal, and multi-word glosses match too ("ice cream" ⇒
"icecream"). Word readings come from the ViewModel's pronunciationOf.
Kana input is unchanged (still scoped to the active Hiragana/Katakana tab).bodyLarge,
16 sp) instead of the oversized title style, so a longer query like
"ka me ra" fits the narrow title slot without the single-line field scrolling
and clipping the first character.SrsScheduler, LessonGenerator,
StudySession, and PracticeActionBar (the Again/Hard/Good/Easy grade bar;
FreePracticeActionBar split into its own file), plus the three
domain/learning test files. SessionPhase collapsed to LOADING/FREE;
the COMPLETE summary, the "Start a study session" / "Start next session" entry
points, and the Library's "Learned/Locked" status labels are gone.default_sets_seeded flag): for each script, the 10 gojūon rows +
the Voiced / Combined / Extended kana categories + starter word sets
(Colors / Nature / Seasons; Food & drink / Countries / Everyday items). They
are ordinary editable sets afterward, and a deleted default does not return.review_cards / character_progress /
vocabulary_progress tables + entities/DAOs + RoomLearningRepository
remain as dormant, unused schema (removing them would need a destructive
migration).ScriptScopeTest to its pure filter/gating
cases; added default-practice-set seeding tests).rememberUpdatedState.MAX_INTERVAL_DAYS
(100 years), preventing integer overflow after very long GOOD/EASY streaks.SharedPreferences disk load off the main thread.ReviewCard (repetitions / due-date semantics).practice_sets + practice_set_items, additive MIGRATION_3_4).
Built in a Library selection mode (tap + drag-range select, list/grid toggle)
and managed in a sets manager (rename / delete / edit members / Quiz / Practice,
a sort menu, and a color-coded per-set average score).StrokeAttribution assigns each stroke to its nearest cell); per-kana
score chips + an average render on the canvas and persist to history.scoringMode / scoreStats preserved across session rebuilds (was silently reset
by "Start next session").introduce + increment run sequentially so the trace count can't be lost.ScriptScope word gating is per-reading-unit, so a mixed dakuten+yōon word (でんしゃ)
is offered only when actually unlockable.KanaUnits groups a base + small kana into one reading unit.KanaKind).activeScript setting, and a Hiragana/Katakana selector in the Library.Each advanced group is off by default (a Library toggle folds it in) so beginners meet only the base gojūon first. The learning engine and Room schema were reused unchanged (new rows are additive; off-scope cards are inert orphans).
startNextSession no longer drops activeScript / opt-in settings on rebuild.domain/scoring/ — geometry form/fit dials + ML Kit
digital-ink recognition; advisory only, never touches SRS), persisted via Room v3.SrsScheduler, simplified SM-2) and session generation
(LessonGenerator) — pure, clock-injected, unit-tested.PracticeViewModel): REVIEWS → NEW/PRACTICE → VOCABULARY
→ COMPLETE → FREE; grades AGAIN/HARD/GOOD/EASY.MIGRATION_1_2: rename practice_attempts →
practice_history, add source, add SRS/progress/vocabulary tables), exportSchema.StrokeAnimationEngine — strokes draw themselves in recorded order, direction, and
rhythm, with visible pen lifts; play / pause / replay / speed (0.5×–2×) / auto-repeat.strokeAnimations data generated offline from the SVG stroke paths; the static
numbered diagram (StrokeOrderDiagram) became the fallback for glyphs without timing.strokeAnimations are consistency-checked against strokes[] and voided (with a
fallback to the static diagram) when malformed.PracticeScreen — character guide (top), a custom Compose
tracing Canvas with a 2×2 genkōyōshi grid + light-grey ghost glyph + stylus/finger
input with pressure capture (middle), and Clear / Next / grade buttons (bottom).StrokePoint / UserStroke / DrawingSession, CanvasState), MVVM
structure, Material 3, minimal Room, and the first 5 hiragana (あいうえお).30f588a Initial commit: Ironlog powerlifting tracker