Ship the code. We draft the thread.

Deployable reads your merged pull requests and drafts the thread, the LinkedIn post, and the changelog entry.

What a draft looks like when it reaches you.

Three surfaces from one pull request, each rendered from the same set of extracted facts so they cannot contradict each other. The drafts below were written by hand to show the format while the live corpus is being generated. The pull requests are real, and every claim carries the file it came from.

fix(etag): resolve incorrect incremental hashing for chunked responses

1/4

If you served streamed responses through Hono's ETag middleware, two different bodies could end up with the same ETag. That is fixed.

2/4

The middleware was hashing the first chunk and treating it as the whole body. Anything that differed only after the first chunk hashed identically.

src/middleware/etag/index.ts
3/4

Hashing is now incremental across every chunk, and the digest is finalised when the stream closes.

src/middleware/etag/index.ts
4/4

The practical effect: a conditional request against a body that actually changed now misses the cache instead of getting a stale 304.

src/middleware/etag/index.test.ts

A subtle caching bug in Hono, now fixed. The ETag middleware hashed only the first chunk of a streamed response and treated that digest as the whole body. Two responses that differed only after the first chunk would produce the same ETag, and a client holding the old copy would get a 304 for content that had changed. Hashing now runs incrementally across every chunk and finalises when the stream closes. If you stream responses and rely on ETags, this is worth picking up.

## ETags are now correct for chunked responses

- The ETag middleware hashed only the first chunk of a streamed response, so two different bodies could share an ETag.
- Hashing now runs incrementally across every chunk, and the digest is finalised once the stream closes.
- A conditional request against a changed streamed body now correctly misses the cache instead of returning 304.
3 claims cited · 1 dropped as ungrounded · illustrative sample

feat: adaptive progressive image loading for photo viewer

1/4

Opening a photo in Immich no longer means staring at an empty frame. The picture shows up right away and sharpens as it loads.

2/4

Before this, the viewer waited for the full-resolution file before drawing anything at all. On a slow connection that was a second or two of blank space, on every photo.

web/src/lib/components/asset-viewer/photo-viewer.svelte
3/4

Now a small preview appears immediately and gets replaced as the real image arrives, so there is always something to look at.

web/src/lib/components/asset-viewer/photo-viewer.svelte
4/4

The preview size adapts to your connection as well, so a slow network is not sent a large file it has no chance of loading quickly.

web/src/lib/utils/asset-utils.ts

A small change, and the kind you feel every single time you use the app. Opening a photo in Immich used to wait for the full-resolution file before drawing anything. On a slower connection that meant a blank frame for a second or two, on every photo you opened, which is the sort of thing people experience as "this app is slow" without being able to say why. The viewer now shows a small preview immediately and sharpens it as the full image arrives. It also picks the preview size based on your connection, so a slow network is not handed a file it cannot download quickly. Perceived speed is mostly about whether something appears, not about how long the whole thing takes.

## Photos now appear the moment you open them

- The viewer used to wait for the full-resolution file before drawing anything, so a slow connection meant a blank frame on every photo you opened.
- A small preview now renders straight away and is replaced as the full image arrives, so there is always something on screen.
- The preview size is chosen from your current connection speed, so a slow network is not handed a large file it cannot download quickly.
3 claims cited · 2 dropped as ungrounded · illustrative sample

Validate content collection reference() IDs after sync

1/4

Astro content collections now catch broken reference() IDs at sync time instead of letting them fail during render.

2/4

Before, a reference pointing at an entry that did not exist passed sync happily. You found out when a page blew up, often on a route you were not editing.

packages/astro/src/content/sync.ts
3/4

Reference IDs are now validated against the target collection while syncing, so a typo fails right away and names both the id and the collection.

packages/astro/src/content/sync.ts
4/4

The error also names the file the bad reference came from, which removes the part where you grep the content tree to find it.

packages/astro/src/content/errors.ts

Astro shipped a small change to content collections that removes a genuinely annoying debugging loop. A reference() pointing at a nonexistent entry used to pass the sync step and only fail at render time, often on a route unrelated to the one you were editing. The stack trace pointed at the renderer rather than the content file. Reference IDs are now validated during sync. A typo fails immediately, and the error names the id, the collection, and the file the reference came from. Shorter feedback loops are most of what good DX is.

## Broken collection references now fail at sync, not at render

- A reference() pointing at an entry that does not exist used to pass sync and only surface as an error when the page rendered.
- Reference IDs are now checked against the target collection during sync, so a typo fails immediately with the offending id and collection named.
- The error names the file the bad reference came from, so you are not searching the content tree for it.
3 claims cited · 0 dropped as ungrounded · illustrative sample

fix(client): serialize multiple cookies correctly

1/3

Hono's client was dropping cookies. If you set more than one, only the last survived the request.

2/3

They were being collapsed into a single header value rather than serialised as separate pairs, so a session cookie plus anything else meant the session cookie lost.

src/client/utils.ts
3/3

Serialisation is fixed, and every cookie you set now arrives. Worth updating if you use the client with authenticated requests.

src/client/utils.ts

A small fix in Hono with an outsized failure mode. The client collapsed multiple cookies into a single header value, so only the last one set actually made it into the request. If you set a session cookie and then a preference cookie, authentication quietly broke, in a way that looks like a server problem rather than a client one. Cookies are now serialised as separate pairs and all of them arrive. The bugs worth writing about are usually the ones that fail silently in the wrong direction.

## The client now sends every cookie, not just the last one

- Multiple cookies were collapsed into a single header value, so only the last one survived the request.
- Cookies are now serialised as separate pairs joined correctly, so a session cookie and a preference cookie both arrive.
2 claims cited · 1 dropped as ungrounded · illustrative sample

From diff to draft, with the receipts attached.

The engine pulls a set of facts out of the diff, and each fact carries a citation to the hunk that proves it. Every surface is then rendered from that same fact set. A faithfulness pass removes any claim it cannot trace back to the code, and tells you how many it removed.

diff --git a/src/queue/consumer.ts
@@ -41,7 +41,22 @@
- await handler(batch)
+ const failed: Message[] = []
+ for (const msg of batch.messages) {
+ try { await handler(msg) }
+ catch { failed.push(msg); msg.retry() }
+ }
+ if (failed.length === batch.messages.length)
+ throw new BatchFailure(failed)

Draftpending your review

1/3

A single bad message used to take down an entire queue batch. Now each message retries on its own, and only a fully failed batch escalates.

2/3

Messages that exhaust their retries land in a dead letter queue rather than disappearing, so nothing is lost while you debug.

src/queue/consumer.ts

Three things happen before a draft reaches you.

Secrets are stripped first

Diffs pass through a secret scanner before any model call. Files that look like credentials are removed rather than summarised, so a key committed by accident does not become training input or a draft.

Unsupported claims are removed

Every generated claim is checked against a cited hunk. Claims that fail are dropped outright rather than softened, and the draft reports the count, so a quiet failure is visible instead of invisible.

Publishing waits for you

The pipeline pauses on a durable approval step that can hold for two weeks. Nothing reaches a timeline, a feed or a changelog until you approve it. This is a workflow with known steps rather than an agent with latitude, which is the reason the pause is reliable.

Most of what you ship is too small to announce.

A dependency bump, a flaky test fixed, an edge case handled. Individually none of it is worth a post, so it goes unwritten. Once a week Deployable collects what nobody would publish on its own and writes it as one piece of narrative, which is the part that changelog generators structurally cannot do.

  • choreBump workerd and workers-types
  • choreFix flaky retry test on Windows
  • bugfixHandle empty batch in queue consumer
  • choreBump undici 7.28 to 7.29
  • docsCorrect typo in binding error copy
  • choreSkip lockfiles when measuring diff size
  • bugfixGuard against zero-length hunk

Weekly rollupone draft

Quiet week on the surface, busy underneath. Queue handling got noticeably more forgiving: an empty batch no longer trips the consumer, and a zero-length hunk no longer throws during diff sizing. Lockfiles are now skipped when measuring diff size, which is the change most people will actually feel, because it stops a dependency bump from looking like a thousand-line pull request.

Deployable sits downstream of your changelog.

Git-native changelog tooling already exists and most of it is free. If a bullet list of merged pull requests is what you need, use one of these instead. The difference is what happens after the changelog.

CapabilityGitHub release notesrelease-pleaseChangelog CMSDeployable
Produces a changelogYesYesBy handYes
Reads the diffNoNoNoYes
Writes in your voiceNoNoNoYes
Thread and LinkedIn postNoNoNoYes
Weekly narrative rollupNoNoNoYes
Cites the code behind a claimLinks the PRLinks the PRNoCites the hunk

Send a repo. We send back drafts.

Point us at a public repository. We run its recent merged pull requests through the engine and send back what it wrote, citations attached, so you can judge the output before installing anything.

Free for open source. Paid when it is a product.

Free

Public repositories, one organisation

A capped number of content pieces each month, including at least one weekly rollup, so the part that makes this different is not behind the paywall. Carries a footer credit.

Billing is not self-serve yet. Pricing is listed so you can decide whether this is worth your time before you talk to anyone.