yousaidthat.org
Prove that you said things before anyone else Discussion | Link
AI人工智能最新资讯、模型发布、研究进展
Prove that you said things before anyone else Discussion | Link
Build AI slide decks without leaving your terminal Discussion | Link
I’m Nick, founder of Drafted ( https://www.drafted.ai ). We’re training models that generate residential architecture from structured design constraints. Product demo: https://www.youtube.com/watch?v=8QkJ7jNU9y4 Residential architecture is still one of the most expensive, slow, and inaccessible creative processes in the world. Designing a custom home typically costs $10,000–$50,000 or more, takes months, and requires making major decisions before most people can even visualize the outcome. As a
Turn your outdoor activities into a territory game. Discussion | Link
See what AI agents do inside your MCP server Discussion | Link
Remembering the ups and downs of the Intel Mac era as it finally winds down.
Series Final. The four mechanisms covered across this series — knowledge graph, Auto Review, Self-Healing, Recurrence Prevention — plus the non-engineer-PR application that sits on top of them, all hang off a single conviction: AI isn't something to trust; it's something to design. The 'I don't trust AI to fill in the blanks for me' framing this lives inside isn't doubt about generation quality, but the clear-eyed acceptance that AI has no idea what context wasn't handed to it, and that 'ideal behavior with no spec given' is a fantasy. The starting point goes back to 2025, when I was trying to figure out how to make AI actually understand a large codebase — and ran into walls on both context window scaling (lost in the middle, attention dilution) and learning-based approaches (machine unlearning, destructive interference). GraphRAG + MCP became the way out: hand AI only the facts it needs, when it needs them, so it doesn't have to infer. From code-graph (which I burned two months on and threw away) to the current product-graph (cpg). This piece is the philosophy and the trial-and-error behind the whole series: harnesses confine where hallucinations are allowed to happen, design is translating principles into your own use cases, and Coverage 90% as a solo target breaks the implementation.
Hi HN! Excited to launch machine0, a CLI that makes it easy to create, provision and snapshot persistent NixOS (& Ubuntu) VMs. You can think of machine0 as a modern VPS provider. VMs stay on unless switched-off (with 99.99% uptime), they have static IPs and HTTPS endpoints, 1-60 vCPU, up to 240GB RAM and optionally GPUs. The CLI provides commands to manage lifecycle, snapshots and also provision the VMs using Nix flakes or Ansible playbooks. VMs are priced by the minute of usage. What makes mach
I built a browser-based desktop environment (IP Linux) with React, TypeScript and Vite I have been working on a project called IP Linux : a browser-based desktop environment that runs as a static web app. Live site: https://ip-os-linux.vercel.app/ GitHub Repository: https://github.com/ikerperez12/IP-OS-LINUX It is not a real Linux distribution, and it does not run native binaries. The idea is different: I wanted to explore how far a polished desktop-like experience can go inside a normal browser tab. The result is a small web OS-style environment with: A splash / entry screen A desktop with icons, folders, and widgets A top panel with system controls A dock and app launcher Resizable and draggable windows Virtual workspaces Snap assist A global search / Spotlight-style command palette Local-first apps (Files, Terminal, settings, player) Reactive wallpapers Glass UI and visual effects Why I built it Most web demos are landing pages, dashboards, or small single-purpose apps. I wanted to build something that feels more like an environment. I was interested in questions like: Can a web app feel physical and desktop-like? How should windows behave inside a browser viewport? How far can local-first storage go before a backend is actually needed? How do you organize many small apps without making the UI messy? IP Linux became a way to test all of that in one project. The app includes a catalog of built-in apps and tools: Files, Terminal, Browser, Settings, App Store, Music Player, Matrix Rain, games, developer tools, productivity apps, and visual utilities. The virtual file system and user preferences are stored locally in the visitor's browser with IndexedDB/localStorage. There is no backend, no account system, and no required environment variables for the public release. Would love to get feedback on the interaction design, responsiveness, or features!
A blunt playbook for devs who don’t want to turn into autocomplete zombies. The first time an AI wrote code for me, I felt like I had unlocked cheat codes for real life. I typed a half-baked function name, hit enter, and suddenly I had a block of code that looked legit. It was magical. The second time, though? It suggested something so catastrophic basically the programming equivalent of pulling the fire alarm that I realized: this thing is less “mentor” and more “overconfident intern who thinks they know pointers but actually just broke prod.” That’s where most of us are right now. AI is everywhere: in our IDEs, our docs, even sneaking into PR reviews. Some days it feels like rocket fuel; other days it feels like an autocomplete with a drinking problem. The tricky part isn’t whether AI is “good” or “bad.” The tricky part is how we, as developers, use it without becoming lazy, dependent, or worse complacent. Because here’s the uncomfortable truth: AI won’t replace you, but bad AI habits absolutely will. TLDR : This article is a survival guide for developers in the AI era. We’ll break down why AI feels both magical and mid, the five switches that make AI actually useful, when to trust and when to verify, how to use AI as a research assistant (not a code monkey), the dangers of autocomplete brain, and a playbook for building a healthy workflow. Why AI feels both magical and mid Every dev I know has had that moment with AI. The first time it autocompleted a function and nailed it, you probably thought: “Wow… this thing just saved me half an hour.” It’s the same dopamine hit as discovering ctrl+r in bash or realizing you can pipe grep into less . Pure wizardry. But the honeymoon ends quickly. The same tool that wrote a clean utility function also happily hallucinates imports that don’t exist, invents APIs, and will confidently explain things that are flat-out wrong. It’s like pair programming with someone who sounds senior but has never actually shipped code. The magic-
I had a write that spanned two physically separate databases. A rename that had to propagate across several tables in one database and a couple of tables in another, and the two had to stay consistent. No distributed transaction coordinator was available to me. So I did the obvious thing: opened a transaction on each, did the work, and committed them one after the other inside a try/catch with rollbacks on both sides. It felt safe. It compiled. It passed tests. Then I drew the failure on a whiteboard, and the safety evaporated. The window that ruins everything Here's the structure, simplified: await using var txA = await dbA . Database . BeginTransactionAsync (); await using var txB = await dbB . Database . BeginTransactionAsync (); await DoWorkOnA ( dbA ); await DoWorkOnB ( dbB ); await txA . CommitAsync (); // <-- succeeds await txB . CommitAsync (); // <-- what if this throws? Two transactions do not make one atomic operation. CommitAsync is a point of no return, and there are two of them. Between the first commit returning and the second one starting, there is a window. If txB fails in that window — the connection drops, the process is killed, the database hiccups — then A is permanently committed and B never happens. Your rollback in the catch block is useless: you can't roll back txA , it's already durable. The two databases now disagree, and nothing in your code will heal that on its own. This is the dual-write problem , and it's not a bug you can fix by being more careful with try/catch. The atomicity you want simply isn't available from two independent commits. Ordering them, nesting them, wrapping them — none of it closes the window, because the window is inherent to having two commit points. Why "it's never failed" isn't reassurance The seductive thing about this pattern is that the window is small, so in practice it almost never triggers. You can run it for a year and never see an inconsistency. That's exactly what makes it dangerous: it trains you to tr
Every developer who has written a Playwright test for OTP verification has written this line: const otp = email . body . match ( / \b\d{6}\b / )?.[ 0 ]; It works. Until it doesn't. The email body changes format. The OTP appears inside an HTML table. The sending service wraps it in a <span> . Your regex matches a phone number instead of the code. The test fails intermittently and you spend an hour debugging something that has nothing to do with the feature you're testing. The regex problem OTP extraction via regex is brittle by nature. You're pattern-matching against a string that your email sending service controls — not you. Any time the template changes, your tests break. Here's what a typical OTP test looks like today: import { test , expect } from ' @playwright/test ' ; import { ZeroDrop } from ' zerodrop-client ' ; const mail = new ZeroDrop (); test ( ' user can verify OTP ' , async ({ page }) => { const inbox = mail . generateInbox (); // 1. Trigger OTP send await page . goto ( ' /login ' ); await page . fill ( ' [data-testid="email"] ' , inbox ); await page . click ( ' [data-testid="submit"] ' ); // 2. Wait for email const email = await mail . waitForLatest ( inbox , { timeout : 15000 }); // 3. Extract OTP — the fragile part const otp = email . body . match ( / \b\d{6}\b / )?.[ 0 ]; if ( ! otp ) throw new Error ( ' OTP not found in email body ' ); // 4. Enter OTP await page . fill ( ' [data-testid="otp"] ' , otp ); await page . click ( ' [data-testid="verify"] ' ); await expect ( page ). toHaveURL ( ' /dashboard ' ); }); The test works — but line 14 is carrying all the risk. Change the email template and the test breaks. Add a phone number to the footer and the regex matches the wrong number. Send a 4-digit OTP instead of 6 and you need to update the pattern. OTP extraction at the edge ZeroDrop extracts OTPs before they reach your test. The Cloudflare Worker that catches incoming emails runs a pattern match on the plain-text body and stores the result alongsi
Notes from building a memory layer that forgets on purpose. Most "memory-enabled" agents don't remember anything. They re-read. Every turn, the whole conversation gets pasted back into the prompt, and we call that memory because the model can answer questions about earlier turns. It's a good trick. I used it for months. It also falls apart the moment real people start using the thing, and it falls apart in three separate ways. The first is the one everyone notices: it's expensive and noisy. You re-send every prior turn on every request. The single line you actually care about - "I'm allergic to peanuts" - is buried under a thousand lines of small talk, and you pay for all of it, every time. The second is quieter and worse. Transcript-stuffing has no idea what stale means. If someone told your agent "I'm vegetarian" in March and "I eat fish now" in May, you've just handed the model both facts with equal weight. Now it has to guess which one is current. Sometimes it guesses wrong, and there's nothing in the system that even thinks that's a problem. The third one is the reason I stopped treating this as a side quest. When you finally add summarization to control the cost from problem one, the summarizer is free to drop whatever it wants to save tokens. Including the allergy. I spent years around fintech, where the wrong record surviving (or the right one quietly vanishing) is how people get hurt, so this landed hard: forgetting an allergy to save 40 tokens isn't a cost bug. It's a safety bug wearing a cost bug's clothes. So the question I actually wanted to answer wasn't "how do I make my agent remember more." It was: how do I build something where acting on a fact the user already retracted and silently dropping a fact that must survive are impossible by construction, not just unlikely if the prompt is good that day. Everyone has already solved one third of this The encouraging part is that you don't have to invent much. The discouraging part is that every existing sy
Most token scam detectors, including the one I work on, share one implicit assumption: the contract you analyze at launch is the contract people will trade. Read the source, simulate a buy and a sell, cluster the deployer, score it, done. That is a snapshot. And a snapshot is exactly what a patient scammer plays against. Two token designs pass every launch-time check and then turn hostile later. This is how they work, and the two on-chain techniques we shipped this week to catch them. Design 1: the delayed honeypot A honeypot is a token you can buy but cannot sell. The classic version is non-sellable from block one, so a buy-then-sell simulation catches it instantly. The patient version is sellable at launch. Early buyers sell fine, the chart looks healthy, the token earns a clean verdict from every checker that judged it at T0. Then, days later, the operator flips a switch: a timed blacklist that rejects transfers after a block height or timestamp, a setTrading(false) / pause() kill switch pulled once liquidity has accumulated, a fee setter cranked to 100% on sells. From that moment it is a honeypot. But the only verdict on record is the clean one from launch day. The detection ran once, at the worst possible time to run it. Fix: re-simulate at J7 We keep post-launch snapshots of every token at J0, J7 and J30 (originally to catch slow rugs: volume collapse, late LP burns). The new piece re-runs the full buy/sell honeypot simulation at J7, but only for tokens that were genuinely sellable at J0. A clean-to-honeypot flip is the signal: // Only for tokens sellable + tradable at J0 - a clean->honeypot flip is the point. // Bounded per run because it is RPC-heavy. const eligible = ! j0 . risk_flags . some (( f ) => J0_SKIP_RESIM_FLAGS . has ( f )); if ( rpc && eligible && resims < resimLimit ) { const isNowHoneypot = await detectLateHoneypot ( rpc , tokenAddress ); if ( isNowHoneypot ) flags . push ( " late_honeypot " ); // +40 risk at J7 } One rule we hold to: an RPC hi
A story about an AI alert model that cut false alarms from 60% to 7% — and what happened when...
Sometimes you genuinely need the set of columns to update to be data, not code. An operator maps configuration fields to database columns, and you want to honor that mapping without redeploying every time it changes. The naive solution — build an UPDATE string from those column names — is also one of the easiest ways to hand-write a SQL injection vulnerability. This is how to get the flexibility without the hole. We'll build it up in three layers: make it work, make it safe, then count the cost. Layer 1: The dynamic update, the wrong way The tempting version concatenates column names into SQL: // DO NOT do this. var sql = $"UPDATE products SET { columnName } = { value } WHERE id = { id } " ; If columnName comes from configuration that an operator can edit, you've just made your schema writable by whoever controls that config. A value of name = 'x'; DROP TABLE products; -- is now your problem. Even "trusted" config is an injection surface the moment it flows into a SQL string. Layer 2: The same feature with EF.Property EF Core's ExecuteUpdateAsync lets you set a property by name without ever building SQL yourself. EF.Property<T> takes the property name as a string, and EF parameterizes the value and validates the property against the model: await db . Products . Where ( p => p . Id == id ) . ExecuteUpdateAsync ( setters => setters . SetProperty ( p => EF . Property < float ?>( p , columnName ), value )); This is already a different security posture: the value is a parameter, not interpolated text, and EF will throw rather than emit SQL if columnName isn't a real mapped property. But "EF will throw" is a runtime backstop, not a policy. We want to reject bad names before they reach the database, fail closed, and control exactly which columns are writable. Layer 3: Reflection as a whitelist The guard is to validate every incoming column name against the entity's actual properties, using reflection, and to keep an explicit blacklist of fields that must never be touched d