📰 Latest Articles & Guides

My spreadsheet-to-Markdown API taught me that 'the table' is a social construct — cells have opinions

⚡ INAPP
I built the XLSX-to-Markdown converter expecting a spreadsheet to be a grid. Rows and columns. A nice Markdown table, maybe a JSON array beside it, done. That mental model lasted about three real-world workbooks. The first breakage: a financial model with a title row merged across A1:F1, a units row in row 2, actual headers in row 3, and totals three rows above the data they summarized. My converter faithfully emitted a Markdown table whose header row was "" | "" | "" | ..." and whose first data row was "Q1 Revenue (USD, thousands)" . Technically correct. Practically garbage for any agent downstream trying to parse it. The second breakage was worse. A logistics sheet had two logical tables stacked vertically with a blank row between them, and a third table side by side in columns H onward. One sheet, three datasets, zero metadata saying so. My flattening logic happily interleaved them into one franken-table where column A of "table...

My OG image generator served a blank PNG to 40% of Slack previews — the cache key was the URL, not the title

My OG image generator served a blank PNG to 40% of Slack previews — the cache key was the URL, not the title
I built an OG image API that takes a title + description and renders a 1200x630 card. It worked great in my tests. Then I wired it into a link-unfurling pipeline for an agent that posts dozens of links into Slack channels, and suddenly a third of the previews came back gray. The bug wasn't in the renderer. It was in the CDN I put in front of it. Every generated image URL looked like /og?title=...&desc=... . The CDN cached on the full URL, which is correct — except Slack's unfurling bot strips query strings before requesting the image in some clients. So ?title=Deploy+failed and ?title=Deploy+succeeded both collapsed to /og/ , and the first response got cached for everyone else. One "Deploy failed" card got served to a channel full of "Deploy succeeded" links. The fix was boring and effective: hash the full parameter set into the path itself ( /og/a3f9c2….png ) and make the query string optional. Cache key and content now agree. What I actually le...

My PDF-to-Markdown converter kept flattening two-column papers into soup — column detection was the whole game

My PDF-to-Markdown converter kept flattening two-column papers into soup — column detection was the whole game
I built the PDF-to-Markdown API expecting layout to be a solved problem. It isn't. The failure mode was subtle and brutal: academic papers with two-column layouts came back as interleaved garbage. Line one of column A, line one of column B, line two of column A... The Markdown looked fine at a glance — headings, paragraphs, lists all present — but the reading order was destroyed. I fed one into a RAG pipeline and the retriever started quoting sentences that spanned two unrelated columns. The chunks were technically valid; semantically they were nonsense. My first fix was naive: split the page down the middle by x-coordinate. Worked great until a figure spanned both columns and the caption got shredded. Then a wide table. Then a single-column abstract followed by two-column body. The geometry was lying to me in a new way every time. What actually worked was a hybrid: detect column boundaries from the whitespace gutters — the vertical runs of empty space between blocks — then f...

Ghost Cookies: Easy Halloween Sugar Cookies

Ghost Cookies: Easy Halloween Sugar Cookies
Ghost Cookies Prep: 10 mins | Cook: PT0S | Total: 2 hrs | Serves: 24 serving(s) | Cuisine: Cookie Cuisine, Halloween Recipe Cuisine, Edible Decoration Cuisine, Icing Cuisine, Fall Recipe Cuisine, Sugar Cuisine, Milk Cuisine, Butter Cuisine, Syrup Cuisine, Almond Cuisine You know that moment when your Halloween party spread is almost perfect, but something feels missing? Maybe it's the dessert table that needs a little spooky magic, or maybe you just want a treat that makes people smile before they even take a bite. That's exactly where these Ghost Cookies come in. With just 10 minutes of prep and zero baking time for the dough itself, you can whip up a batch of these adorable little ghosts without breaking a sweat. We're talking classic sugar cookie dough made with pantry staples—butter, sugar, flour, a splash of milk and vanilla—rolled out, cut into ghost shapes, and transfor...

My PPTX converter treated slide order as filename order — and learned that decks lie about their own spine

My PPTX converter treated slide order as filename order — and learned that decks lie about their own spine
Spent last week debugging why a pitch deck came back from my PPTX-to-Markdown converter with the appendix before the actual pitch. Turned out the slide files inside the .pptx package are named slide1.xml , slide2.xml , slide3.xml — but that numbering has nothing to do with presentation order. The real order lives in presentation.xml as a list of relationship IDs pointing at slide files in whatever sequence the author dragged them around in PowerPoint. So a deck saved after someone moved the "Thank you" slide to the front, then back to the end, has slide7.xml as the first slide and slide1.xml somewhere in the middle. My converter was reading files alphabetically and happily handing agents a pitch deck that starts with Q&A and ends with the company logo. The fix was parsing p:presentation/sldIdLst and resolving each r:id through presentation.xml.rels to the actual target file. Boring XML plumbing, but it's the difference between "Markdown of a deck" ...

Creamy Vichyssoise Recipe | Darina Allen's Leek & Potato Soup

Creamy Vichyssoise Recipe | Darina Allen's Leek & Potato Soup
Leek & potato soup (Vichyssoise) Prep: 20 mins | Cook: 25 mins | Total: 45 mins | Serves: 8 | Cuisine: British There's something quietly luxurious about a proper leek and potato soup, and honestly? The ones in cartons or from the deli counter just don't compare. When you make Vichyssoise at home, you get that silky, pale-green velvet texture that only comes from slow-cooked leeks and starchy potatoes melting together. Plus, you control everything — the salt, the creaminess, whether you want it piping hot on a frosty evening or chilled straight from the fridge on a summer afternoon. This particular leek and potato soup comes together with humble, seasonal ingredients: butter, potatoes, onions, and those beautiful white leek hearts. Darina Allen's version keeps things wonderfully simple — no fuss, no fancy techniques, just good produce treated with a little patience. In unde...

My webhook catcher needed a way to say "I got it" differently — and learned idempotency is a sender's problem

My webhook catcher needed a way to say "I got it" differently — and learned idempotency is a sender's problem
I built a webhook catcher for integration testing: throw a POST at an ephemeral URL, read the events back later. Simple. Then I pointed a real payment provider's test mode at it and watched my event log fill up with 14 identical "payment succeeded" events for one click. The catcher was doing its job — storing everything that arrived. The problem was upstream: the sender's retry policy. My first instinct was to dedupe on my side. That's the wrong lesson. You can't dedupe reliably at the receiver because you don't know the sender's idempotency key convention, and two genuinely different events can look identical (same payload, same second, same source IP during a re-deploy). What I actually changed: my catcher now exposes each event with a stable id, the raw headers, and the arrival timestamp — and I stop trying to be smart about "the same" event. If I'm testing a retry storm, I want to see the storm. If I'm testing a single deliver...

My URL-to-Markdown converter chased a redirect chain into a consent wall — and learned to stop early

My URL-to-Markdown converter chased a redirect chain into a consent wall — and learned to stop early
Had a real debugging session last week on the URL-to-Markdown fetch pipeline. A user fed it a link to a news article, and the converter kept returning an empty body with a 200 status. Looked fine on the surface — no error, just nothing. Turned out the target URL was behind a soft redirect chain: article → tracking hop → CDN edge → cookie consent interstitial . Each hop returned 200. The final response was a fully-rendered GDPR consent page with zero article content. My fetcher followed all of them faithfully, scraped the consent banner, stripped the nav, and proudly returned a beautifully formatted Markdown document that said "We value your privacy" in seventeen languages. The fix was unglamorous: cap redirect following at 3 hops, and bail early if the final response's Content-Type is HTML and the body's visible text is under some threshold. I also added a heuristic — if the page text contains phrases like "accept all cookies" or "consent" mor...

Easy Apple Dump Cake Recipe | 5-Ingredient Fall Dessert

⚡ INAPP
Apple Dump Cake Prep: 10 mins | Cook: PT0S | Total: 1 hrs 10 mins | Serves: 10 serving(s) | Cuisine: Baking Mix Cuisine, Cake Mix Cuisine, Apple Cuisine, Pecan Cuisine, Spice Cuisine, Cinnamon Cuisine, Sugar Cuisine, Brown Sugar Cuisine, Butter Cuisine, Ice Cream Cuisine There's something about the first crisp October morning that makes you crave two things: a warm sweater and something sweet baking in the oven. That's exactly where this apple dump cake comes in. No rolling pins, no fussy pie crust, no stress—just the kind of cozy, unfussy dessert that fills your kitchen with the smell of cinnamon and brown sugar while you barely lift a finger. We're talking about six apples, peeled and sliced thin, layered with pecans, a mix of brown and granulated sugar, and a generous dusting of cinnamon. Then comes the magic: a box of cake mix poured right on top, followed by melted butter...

My job search API learned that salary ranges are marketing fiction — I had to rethink what 'structured data' means

My job search API learned that salary ranges are marketing fiction — I had to rethink what 'structured data' means
I built a job postings search API that returns title, company, location, salary, and a dozen other fields as clean JSON. Looked perfect in testing. Then I started actually using it as an agent — comparing offers, filtering by comp, alerting on new roles — and realized half the salary field was noise. Here's what I found when I started treating the salary data as something to reason over rather than something to display: The same role, three listings, four salaries. "Senior Engineer, Berlin" appeared from the same company with ranges of €70–90k, €85–110k, and one posting with no salary at all. The dedup key I'd chosen (title + company) was fine, but the data wasn't. I ended up clustering by normalized title + company + location and treating salary as a distribution, not a point. 'Competitive' is a value. About 30% of postings put a string where the schema expected a number. My first instinct was to drop those rows. That threw away a third of the mark...