Lossless Changelog
product

Curation you can leave and come back to

Approving 80 sources is not one sitting's work. The surface now autosaves, shows its progress, opens sources without waiting on a browser, and makes a pasted link name itself.

Michael StatonMusic Editoraugmented with Claude Code on Claude Opus 5 (1M context)

Why Care?

The source-approval surface shipped two days ago and immediately met a real deal: ImmuneCo, 79 sources. Three things broke down at that scale that looked fine at three.

You cannot review 79 sources in one sitting, and the only way to save was a button you had to remember to press. Opening a source handed off to a browser already carrying a day's worth of tabs, which is slow enough to discourage the checking the surface exists to encourage. And a pasted link showed up in the list as a16z.com — while the preview pane one row below displayed its actual title.

None of these are edge cases. They are what the surface is at working scale.

What's New?

  • Autosave, debounced, with a visible save state and a progress bar.

  • Quick look — a reusable in-app window that renders any source instantly.

  • Metadata on paste — an added link fetches its own title and date.

  • A clean build — 1 error / 24 warnings → 0 / 0.

Leave and come back

Every verdict, add, removal, and accepted URL-swap now schedules a debounced write. Navigating away flushes first. The header carries a state you never have to guess at:

✓ Saved 12s ago   ·   Saving…   ·   Unsaved changes
34 of 79 reviewed  ▓▓▓▓▓▓▓░░░░░░░  resume where you left off →

Two rules make it trustworthy rather than merely present. A failed autosave does not clear the dirty flag — telling you your work is safe when it isn't is the one lie this surface cannot afford, so it says so instead. And autosave never codifies: it writes mode: aggregated, because arming the membership gate against a half-reviewed set would be worse than not saving at all.

Autosave plus always-backup would have buried the deal directory in .bak- files within one session. The standalone curate_sources.py had already solved that, so we took its rule: an autosave backs up only the first time it touches a deal. Six autosaves, one backup.

Why the obvious answer was wrong

The natural fix for "opening sources is slow" is an inline iframe. We measured before building, against the first 24 real ImmuneCo sources:

FRAMEABLE: 10    BLOCKED: 14    (of 24)

BLOCKED  nejm.org · thelancet.com · science.org · pmc.ncbi.nlm.nih.gov
         who.int · epa.gov · forbes.com · finance.yahoo.com …

58% send X-Frame-Options, and they are the institutional sources most worth verifying. Worse, cross-origin that failure is silent — no usable onerror, no readable contentDocument — so the UI could not even admit it had failed. It would render blank boxes for the journals and work fine for the blogs.

A second Tauri webview is a top-level navigation, so frame policy never applies: 100% render, and WebKit is already resident so there is no cold start. One reused window, close-and-recreate, closed on unmount. ↗ stays as the escape hatch to a real browser with your logins and paywall subscriptions.

The non-obvious part is the ACL: capabilities/default.json is scoped per window label, so quick-look needs its own entry plus core:webview:allow-create-webview-window. Without it, creation is denied, the fallback fires, and it silently opens your browser — looking exactly like the feature not working. It requires a full dev:native restart; Vite HMR will not pick up a Rust capability change.

Pasting an a16z article produced a row reading a16z.com, while the pane below it showed Title: GMV Retention: The Marketplace Metric Most Ignore. The fetch and the parse were both fine — the metadata simply never reached the row. Three defects on one path: add() fetched nothing, preview() learned the title and discarded it, and the response carried Jina's raw preamble.

That is more than cosmetic. Re-search needs a title and returns None without one, so every pasted source was born un-recoverable; the file would land as …_a16z-com-gmv-retention….md; and 80 rows reading cbinsights.com are unscannable, which is the original complaint reintroduced.

Now paste fires the cheap tier (gated at 3 concurrent), both fetch paths merge through one applyFetchedMetadata() so they cannot drift, and analyst-typed titles always win over scraped ones. Written up in [[Pasted-Link-Shows-Host-Instead-Of-Title]] and [[Metadata-On-Paste-For-Source-Curation]].

The build is quiet now

A bun run dev:native was printing ten warnings, all pre-existing. Cleared them — and one turned out to be a real bug rather than lint noise.

All three modals had role="dialog" on the backdrop, so a screen reader announced the dimming overlay as the dialog and the actual panel had no dialog semantics at all. Svelte was flagging a symptom of genuinely wrong markup. Moved the role, aria-modal, and label onto the .modal panel; the backdrop became role="presentation", which is what makes click-without-keydown correct rather than suppressed. All three already had window-level Escape.

The autofocus warnings were suppressed with justification, not obeyed — the rule targets autofocus on page load, but the WAI dialog pattern expects focus to move into a dialog on open. Degrading that to satisfy a misfiring lint would make the app worse.

The last four looked like more dead CSS but were grouped selectors — button.link, button.inline-link { … } — where only the first half was dead. Bulk-deleting would have silently broken working styles with no test to catch it.

1 error / 24 warnings / 8 files → 0 / 0 / 0. The payoff is that the next warning is a real one instead of the 25th in a wall you have learned to scroll past.

What's Next

The human gate is still unrun: no approved set has been committed through the UI yet, and no run has been constrained by one. ImmuneCo is sitting at 79 sources with the surface working.

Enforcement still defaults to flag. Before flipping it, note the counterweight — Citation-Coverage-Promoter.md measured 7-of-33 curated sources actually cited before any ceiling existed. Enforcement is the ceiling; that plan is the floor.