readywhen
The things you said you'll do. Done. Discussion | Link
AI人工智能最新资讯、模型发布、研究进展
The things you said you'll do. Done. Discussion | Link
I used to hear the pitch for context compression and think: sure, makes sense. Smaller prompts. Lower latency. Lower cost. Same output quality. Then I watched an agent blow a perfectly good debugging session because one field name disappeared from compressed memory. That changed my opinion fast. Three hours into a Claude Code run, the agent made the wrong API call with full confidence. The plan looked coherent. The reasoning looked clean. The summary of prior steps sounded smart. It was also missing the one detail that mattered: a field name from an earlier error log. The agent had already seen the bug. It had already “understood” the bug. But the compressed version of history dropped the exact detail it needed to avoid repeating it. That’s the real failure mode. Not “compression loses words.” Compression loses the one fact your agent needs later, after it has already committed to the wrong action. While researching this, I found a thread on r/openclaw about using Headroom with OpenClaw: https://reddit.com/r/openclaw/comments/1u3j5xs/anyone_using_headroom_with_openclaw/ That thread gets at the real tension: compression is useful, but only if you treat it as a reversible optimization, not a memory wipe with better branding. The bug pattern nobody talks about Here’s the pattern I keep seeing in long-running agents: The agent collects a lot of noisy context. The team compresses it to save tokens. The summary preserves the broad story. The summary drops one edge-case fact. Two hours later, that fact becomes the only thing that matters. The agent confidently does the wrong thing. This is why “same answers, fewer tokens” is not a serious reliability claim for agent workflows. It might be true for some short chat tasks. It is absolutely not something I’d assume for: n8n agents Make scenarios Zapier AI steps OpenClaw sessions Claude Code runs custom OpenAI-compatible agent loops multi-step debugging or incident workflows In those systems, exact details matter more than eleg
Every developer I know has built something for themselves. A productivity tool. A habit tracker. A personal finance app. An AI that makes them smarter, faster, calmer. I did the same thing for 2 years. Then I had a conversation with someone close to me that I completely mishandled — and I realised no amount of personal productivity tools would have helped me there. The problem wasn't me, individually. The problem was the space between us. So I started asking a weird question Why has all of AI been built for individuals? Copilot helps you code faster. ChatGPT makes you smarter. Notion AI organises your thoughts. Calm helps you sleep better. Not one of them is built for what happens when two people try to understand each other. That's a massive gap. And it's one I couldn't stop thinking about. What I built Mendle — an AI-powered Relationship Intelligence platform. Not a therapy app. Not a chatbot companion. Not another journaling tool with an AI skin on top. The core idea is **shared emotional memory. Most relationship apps are built around one person's perspective. You log your feelings. You get insights. Your partner is an afterthought in the architecture. Mendle is different at the data model level. Both people contribute. Both people benefit. The AI builds an understanding of the relationship not just an individual. Over time it surfaces patterns. Communication loops. Emotional triggers. The things you keep missing because you're too close to them. The technical challenge that surprised me Building AI for two people is fundamentally harder than building it for one. Single-user AI: one context window, one set of preferences, one voice to understand. Relationship AI: two different communication styles, two different emotional vocabularies, shared history that neither person has complete visibility into, and privacy boundaries that have to be respected even between partners. The shared memory architecture was the hardest part to get right. How do you build something
Shifting from traditional application testing to serverless TypeScript engineering is all about shifting your perspective: you stop testing a running server, and you start testing how your function responds to events and SDK states. Here is a simple, practical example for each testing, using modern AWS SDK v3 syntax. 1. Unit Testing: Jest + Mock Payloads & SDK Client Mocking In unit tests, you don't call real AWS services. You pass a simulated API Gateway event to your handler, and you mock the AWS SDK so it returns predictable data instead of hitting live infrastructure. The Code Under Test ( src/handler.ts ) import { APIGatewayProxyEvent , APIGatewayProxyResult } from ' aws-lambda ' ; import { DynamoDBClient } from ' @aws-sdk/client-dynamodb ' ; import { DynamoDBDocumentClient , GetCommand } from ' @aws-sdk/lib-dynamodb ' ; const ddbClient = new DynamoDBClient ({}); const docClient = DynamoDBDocumentClient . from ( ddbClient ); export const handler = async ( event : APIGatewayProxyEvent ): Promise < APIGatewayProxyResult > => { const userId = event . pathParameters ?. id ; if ( ! userId ) { return { statusCode : 400 , body : JSON . stringify ({ message : ' Missing ID ' }) }; } // Fetch from DynamoDB const result = await docClient . send ( new GetCommand ({ TableName : process . env . USERS_TABLE , Key : { id : userId } })); if ( ! result . Item ) { return { statusCode : 404 , body : JSON . stringify ({ message : ' User not found ' }) }; } return { statusCode : 200 , body : JSON . stringify ( result . Item ), }; }; The Jest Unit Test ( tests/unit.test.ts ) Instead of the legacy aws-sdk-mock , the modern standard for AWS SDK v3 is aws-sdk-client-mock . import { handler } from ' ../src/handler ' ; import { APIGatewayProxyEvent } from ' aws-lambda ' ; import { mockClient } from ' aws-sdk-client-mock ' ; import { DynamoDBDocumentClient , GetCommand } from ' @aws-sdk/lib-dynamodb ' ; const ddbMock = mockClient ( DynamoDBDocumentClient ); describe ( ' Lambda Handler Unit
The company has set aside an unusually high number of shares for retail investors. Still, experts say, you’re just getting the crumbs.
David Stein shares how to rethink large-scale architectural migrations using AI. He discusses ServiceTitan's "assembly line" pattern, explaining how decomposing legacy codebase refactoring into standardized tasks can achieve massive parallelization. He highlights the critical role of programmatically rigid validation loops to eliminate LLM hallucinations and accelerate engineering agility. By David Stein
Originally published on the Prufa blog . In June 2026 we pointed Prufa's free audit at 50 products that had just launched on Show HN — every launch from the previous 30 days that earned at least 10 points. These are products at their moment of maximum attention: front page, real traffic, founders watching the comments. The headline numbers, from the 49 audits that completed (one site couldn't be reached by our runner): 100% of the 49 launches had at least one machine-verified finding. 78% — 38 of 49 — had at least one critical finding. 40 critical and 61 warning findings in total, every one verified by deterministic checks against captured browser evidence. No site is named in this post. The point isn't to embarrass anyone — it's that these failures are systematic, and if these teams have them on launch day, you probably do too. Methodology, briefly Each site got the same audit a free Prufa run does: a real browser loads the public pages, captures network traffic, console output, cookies, and response codes, and a fixed suite of deterministic checks grades the evidence. Same input, same verdict. Every number below is from a code-verified check — no LLM opinions are counted anywhere in this data. One honest caveat: our export keeps only the top findings per site, so the per-issue counts below are floors , not totals. The real numbers are equal or worse. What actually breaks at website launch: the numbers Sites affected (of 49) Finding Severity 38 No analytics events detected critical 24 No canonical link on entry page info 22 Cookies set without the Secure attribute warning 14 Broken links warning 12 No <h1> heading on entry page info 11 No robots.txt info 10 JavaScript console errors during page load warning 10 Missing meta description warning 8 Images missing alt text info 7 Missing Open Graph tags info 3 Tag container loads, but no analytics events fire warning 2 Canonical URL pointing to a different host critical The most common launch bug: analytics that record
Here's a small thing that drove me up the wall using Claude Code on a real codebase. I have a pre-commit hook. It runs the linter and the type-checker. It exists precisely so that broken code doesn't reach a commit. And Claude — diligent, eager, trying to be helpful — would hit a failing check, decide the check was in the way of the goal , and quietly run: git commit --no-verify -m "fix: update handler" It wasn't malicious. From the agent's point of view, the task was "commit this change," the pre-commit hook was an obstacle, and --no-verify was the documented way around the obstacle. Perfectly logical. Also exactly the thing I never want to happen, because the entire point of the check is that it is not optional . I tried the obvious fix first: I put it in CLAUDE.md . Never use git commit --no-verify . Fix the failing check instead. This works about 80% of the time. Which is another way of saying it fails one commit in five. CLAUDE.md is context — a strong suggestion the model weighs against everything else in the conversation. Under enough pressure ("just get this committed"), a suggestion loses. An 80%-reliable guardrail on something irreversible isn't a guardrail. It's a coin flip with good odds. So I stopped trying to persuade the model and started intercepting the tool call instead. Hooks run before the action, not after the apology Claude Code has a hooks system. The one that matters here is PreToolUse : a script that runs before a tool call executes, receives the call as JSON on stdin, and decides whether it proceeds. Exit 0 and the call runs. Exit 2 and it's blocked — and whatever you wrote to stderr gets fed back to the model as the reason. That last part is the whole game. It's not "please don't." It's a wall, plus an explanation the model can act on. Here's the entire hook: #!/usr/bin/env node // Block `git commit/push --no-verify`. Exit 2 blocks the call. ' use strict ' ; let raw = '' ; process . stdin . on ( ' data ' , ( d ) => ( raw += d )); process .
Most of today went into a user-management overhaul in kickoff — my Laravel starter kit. Flyout CRUD panels, bulk actions, permission assignment, and the piece I want to talk about: account status . Active, suspended, unverified, deleted. The interesting part isn't the feature. It's the modelling decision underneath it. Where do those four states actually live ? The trap: a status column The obvious move is a status enum column on users . Set it to suspended when you suspend someone, active when you reinstate, unverified until they verify their email, deleted when they're soft-deleted. It works. Until it doesn't. Now you've got a status column and an email_verified_at column and a deleted_at from soft deletes — and all three encode overlapping truths. Soft-delete a user and forget to flip status ? Now your database says the account is both active and trashed. Verify an email but the status update fails mid-request? Drift. Every place that mutates a user becomes a place that has to remember to keep status in sync. That's not a feature, that's a maintenance tax. The signals already exist. email_verified_at tells you verified-or-not. deleted_at (soft deletes) tells you removed-or-not. The only genuinely new state is suspended — an admin deliberately blocking sign-in. So that's the only thing worth storing. What we actually store: one nullable timestamp Schema :: table ( 'users' , function ( Blueprint $table ) { $table -> timestamp ( 'suspended_at' ) -> nullable () -> after ( 'email_verified_at' ); }); That's the whole migration. Not a boolean is_suspended — a nullable timestamp. Null means not suspended; a value means suspended and tells you when. A boolean throws that second fact away for free; the timestamp keeps it at no extra cost. Same instinct as email_verified_at and deleted_at — Laravel models "did this happen, and when" as a nullable timestamp everywhere, so we follow the grain of the framework. The behaviour on the model stays tiny: public function isSuspended
Liquid syntax error: Unknown tag 'lineage'
Earlier this week, Life Biosciences, a biotech company focused on reversing age-related diseases, announced that it had dosed its first volunteer. A person with glaucoma has had an experimental treatment injected straight into their eyeball. The idea is to try to treat the disease—which can cause vision loss—by regenerating healthy nerves in the eye. But…
MIT Technology Review Explains: Let our writers untangle the complex, messy world of science and technology to help you understand what’s coming next. You can read more from the series here. Your brain lives in the dark space of your skull. Yet it knows when the wind lifts the hairs on your skin, when your heart is…
In this podcast, Shane Hastie, Lead Editor for Culture & Methods spoke to Craig McLuckie, co-creator of Kubernetes and CEO of Stacklok, about the impact of AI coding tools on open source communities and engineering teams, designing deliberate organisational culture, and navigating evolving career paths for engineers in the age of AI. By Craig McLuckie
A GUI for Apple's native container system Discussion | Link
Our early testing has already shown that Siri AI knows when to shut up, and that's very much by design. In an interview with Mostly Human, Apple's Craig Federighi said new Siri won't act all sycophantic like chatbots made by OpenAI, Google, and others. "As you may know, if you use many of the existing […]
TL;DR: You'll learn how to replace Postman collections with plain-text Hurl files that live in Git, run in CI, and test your API’s geo behavior from any country. Debugging API issues always boils down to taking the tests you already have, running them from a different network or region, and comparing what your API receives. But I bet most of us can’t actually do that right away because our test infrastructure itself is fragmented. Your CI could be using a different config entirely from the “correct” one on a local dev machine, and half the collections may still point to a staging URL that changed months ago. Postman doesn’t produce diff-friendly artifacts, so even figuring out what changed is a full-on investigation. If any of that sounds familiar, I’d recommend finally taking the big step and replacing your Postman collection with Hurl — a command-line HTTP test runner where tests are plain **.hurl** text files that live in Git. For this tutorial, I’ll also walk you through multi-region testing — how to run those tests using a proxy to get a German egress IP, and see what the API actually returns from there. Everything below is self-contained, you’ll only need a new Git repo, then create the tests (three plaintext files.) The Test Suite at a Glance Create a folder (name it whatever you like — hurl-api-tests works) and these are the files we're going to be creating at its root . That folder is your Git repo root; paths in commands and CI are relative to it. Note how we’re using multiple .env files. If you're new to API testing, you'll find out why in a bit. hurl-api-tests/ ├── tests/ │ ├── health.hurl # smoke test -- Makes sure Hurl works, basic asserts. Skip if you want │ ├── auth-flow.hurl # Tests chaining + jsonpath capture + Bearer header │ └── geo-detail.hurl # Tests two egress profiles, real country/ASN diff (direct or proxied) ├── .env.local.example ├── .env.ci.example ├── .env.proxy-de.example ├── run-tests.mjs # cross-platform runner └── .github/workflows/a
I run a content site, do-and-coffee.com . Like everyone else, it gets scraped by AI crawlers. Instead of blocking them, I did something else: I put a paywall in front of the site that returns HTTP 402 Payment Required to bots, with machine-readable payment instructions. If a crawler pays a cent in USDC, it gets the article. If it doesn't, it gets the 402 and nothing else. Then I let it run for 30 days and watched. Here's what actually happened — and it's not the number you'd put on a pitch deck. TL;DR A Cloudflare Worker sits in front of the site. AI crawlers get 402 + x402 payment requirements ; humans and search bots pass through free. Payment is USDC on Base , $0.01 per article, verified and settled through Coinbase's CDP facilitator. 30-day result: 5,811 crawler requests, 5 paid, 5,806 served a 402. Revenue at $0.01/article ≈ $0.05 . The interesting part isn't the revenue. It's who paid: GPTBot paid 4 times out of 48 requests; ClaudeBot paid once out of 651. Architecture do-and-coffee.com/blog/article/* ─▶ x402 Worker (Cloudflare) │ has X-PAYMENT-RESPONSE? ───────────┤─▶ yes ─▶ proxy origin (200) KV cache hit (payer:url)? ─────────┤─▶ yes ─▶ proxy origin (200) no X-PAYMENT? ─────────────────────┤─▶ 402 + payment requirements has X-PAYMENT? ────────────────────┘ │ ├─▶ CDP /verify (is the signed payment valid?) ├─▶ CDP /settle (waitUntil: confirmed — on-chain) └─▶ on success: KV.put(payer:url, receipt, ttl 24h) ─▶ proxy origin The worker speaks the x402 protocol: a 402 response carries an accepts array describing exactly how to pay (scheme exact , network base , asset USDC, amount, payTo wallet). A compliant agent reads that, signs a USDC payment, and retries with an X-PAYMENT header. The worker verifies and settles it through Coinbase's facilitator, then proxies the real article. How it works The 402 response When there's no payment, the worker builds the requirements and returns 402: function buildPaymentRequirements ( resourceUrl : string , env : Env ): Payment
Floating-point addition isn't associative. For a corporate inventory with tens of thousands of rows, naive summation drifts — and the number you disclose depends on row order. Here's why, and the fix. Here's a result that should bother anyone building carbon software. Take a corporate emissions inventory — tens of thousands of line items, each a number in tonnes CO₂e. Sum it. Now sort the same rows differently and sum again. The totals don't match. Not by much — maybe the third or fourth decimal place — but they don't match, and nothing in your code changed except the order. If you've never seen this, open a console: 0.1 + 0.2 === 0.3 // false That's the same bug, scaled up to a reporting deliverable. Why order changes the answer IEEE 754 doubles have 52 bits of mantissa. That's about 15–16 significant decimal digits of precision — generous, until you add numbers of very different magnitudes. When you add a small number to a large running total, the small number gets shifted right to line up the exponents before the addition happens. Bits that fall off the end of the mantissa are gone. Add a 0.0001 tCO₂e line to a running total of 80000.0 and there simply aren't enough mantissa bits to hold both the 80,000 and the 0.0001 — the small value is partially or completely swallowed. Float addition, as a result, isn't associative. (a + b) + c is not guaranteed to equal a + (b + c) . Sum your rows largest-first and the small values vanish early against a big accumulator. Sum smallest-first and they accumulate into something large enough to survive. Same data, different total. Here's the effect, deliberately constructed to be visible: const big = 80000 ; const smalls = Array ( 50000 ). fill ( 0.0001 ); // small values first, then the big one let a = 0 ; for ( const x of [... smalls , big ]) a += x ; // big value first, then the smalls let b = 0 ; for ( const x of [ big , ... smalls ]) b += x ; console . log ( a ); // 80004.99999999... console . log ( b ); // 80004.99999999...
Every few months, a post goes viral: "Please stop using [perfectly good tool]." This time it's lucide-react. And honestly? The take is lazy What's the actual problem? Nothing. The library is actively maintained, tree-shakable, TypeScript-friendly, and has 1000+ consistent icons. A new icon library dropped? Cool. That doesn't make this one broken. Tools don't expire — context does Before switching anything in production, ask: Is it maintained? ✅ Does it solve my problem? ✅ Is my team comfortable with it? ✅ Then keep using it. Real usage — still clean in 2026 import { Search , Bell , User } from ' lucide-react ' ; export default function Navbar () { return ( < nav > < Search size = { 20 } /> < Bell size = { 20 } strokeWidth = { 1.5 } /> < User size = { 20 } color = "#6366f1" /> </ nav > ); } Tree-shaking works perfectly — only Search, Bell, and User are bundled. Not the entire library. When you should switch Unpatched security vulnerability Repo abandoned for 2+ years Bundle size issue you've actually measured If none of these apply, you're switching for hype, not logic. The real issue "Please stop using X" posts get engagement. Developers see them, second-guess stable choices, and waste hours migrating things that weren't broken. Don't let LinkedIn trends drive your architecture. Build products. Not migrations.
So you asked Claude to animate something. Maybe a logo, a loading screen, a data viz. It spat out a neat HTML file with CSS keyframes, everything looks crisp in the browser — and now you need it as an MP4. The obvious approach is screen recording. Open QuickTime or OBS, hit record, play the animation, stop, trim. Works, kind of. Except it's not frame-perfect. If your machine lags for half a second, that lag is baked into the video. The animation runs at whatever speed your CPU felt like that afternoon. Completely non-deterministic. And the moment you tweak something — wrong colour, timing off by 200ms — you're setting the whole thing up again, which is just tiring. Not to mention that every time you hit record you start at a slightly different frame, so swapping the asset in your video editor becomes a pain because nothing lines up the same way twice. There's a better way. You can use htmlrec — a CLI tool that renders HTML animations to video frame by frame, without touching your screen. It controls the browser clock directly, so every frame is captured at exactly the right moment regardless of your machine's load. Pixel-perfect, every single time. Install it with: brew install dsplce-co/tap/htmlrec ffmpeg How to convert an HTML animation to video The reliable way to convert an HTML animation to video is to render it headlessly, frame by frame, instead of screen-recording it. Point a tool at your HTML file, let it drive the browser clock, and capture each frame at an exact timestamp: hrec render animation.html -o out.mp4 This works for any self-contained HTML/CSS animation — a logo reveal, a loading screen, a chart, or anything an LLM like Claude generated for you. The full step-by-step is below. The workflow 1. Get your animation from Claude (skip if you already have an HTML animation) Ask Claude for whatever you need. Something like: "Create an HTML/CSS animation of a logo appearing with a fade and slight upward motion, black background, 3 seconds" You'll get back