AI 资讯
Opinionated by Design: Why I Chose Sensible Defaults Over Endless Configuration
When people hear about a new project scaffolding tool, one of the first questions they ask is: "Can I choose React Query or TanStack Query?" "What about pnpm instead of Bun?" "Can I use ESLint instead of Biome?" "Can I choose Radix instead of Base UI?" "Can I skip Tailwind?" These are reasonable questions. In fact, I asked myself the same ones while building create-notils . My first instinct was to make everything configurable. The more I thought about it, the more I realized I was about to build something I didn't actually want to use. The Configuration Trap Most project generators start simple. Then someone requests another option. Another package manager. Another ORM. Another authentication provider. Another CSS framework. Another UI library. Eventually the CLI starts looking like this: ? Which package manager? ❯ npm pnpm yarn bun ? Which CSS framework? ? Which ORM? ? Which auth library? ? Which formatter? ? Which icon library? ? Which deployment target? It feels flexible. But every new option creates more combinations to support. Five choices in one prompt don't create five possible projects. They multiply with every other prompt. The complexity grows much faster than the number of features. I Built the Tool I Wanted to Use One thing I've learned from building side projects is this: the first user should always be yourself. Every project I start today uses almost exactly the same stack: Next.js 16 React 19 Bun Tailwind CSS v4 shadcn/ui Base UI Biome TypeScript Turborepo (when needed) I wasn't switching between ten different combinations every week. I was rebuilding the same foundation over and over. So instead of asking twenty questions during scaffolding, I decided to optimize for the workflow I actually have. npx create-notils my-app A few seconds later, I'm writing features instead of answering prompts. Opinionated Doesn't Mean Closed There's an important distinction between opinionated and restrictive . Some tools hide their implementation behind abstraction
AI 资讯
I lint-scanned 36 popular MCP servers. A third of them are failing your agent.
Originally published at tengli.dev Your MCP server can be 100% spec-compliant and still be unusable by an agent. The Model Context Protocol spec tells you how to transport tools: JSON-RPC framing, capability negotiation, schema shapes. It says nothing about whether a model can actually use what you serve — whether it picks the right tool out of your catalog, fills the arguments correctly, or burns 8k tokens parsing your schemas on every single request. I integrate first- and third-party MCP connectors into a production AI agent for a living, and I kept seeing the same failure: servers that pass every compliance check, yet the model calls the wrong tool, hallucinates arguments, or ignores the tool entirely. The problems were never in the protocol layer. They were in the parts no one lints: descriptions, naming, schema design. So I wrote mcpgrade — a Lighthouse-style scorecard for MCP servers. One command, no API key, report in seconds: npx mcpgrade --stdio "npx -y your-mcp-server" Then I pointed it at 36 popular servers. It did not go great. The results Full sortable table: https://tengli.dev/mcp-leaderboard.html . The short version (static analysis, point-in-time snapshot; servers marked (archived) are unmaintained reference implementations, included because they're still widely installed and copied): Top of the class (A): brave-search (archived) , exa, google-maps (archived) , slack (archived) , perplexity-ask, @shopify/dev-mcp, @apify/actors-mcp-server, airbnb, figma-developer-mcp, tavily, gitlab (archived) , elastic, shrimp-task-manager, and more — 15 of 36. Bottom of the class (D/F), 11 of 36 — and it's not hobby projects: MongoDB's official server (66, with 66 errors), Notion's official server (62), Airtable (69, 66 errors), todoist-mcp-server (67, 110 errors ), GitHub's archived reference server (67, 44 errors), and firecrawl-mcp at the very bottom (57, 134 errors ). Two more servers (Stripe, Supabase) couldn't be scanned with dummy credentials and were exclud
AI 资讯
HarnessRouter
Bring the world's best AI agents into your app, with one API Discussion | Link
AI 资讯
AI agents are about to rediscover the oldest risk in modern finance
On 26 June 1974, German regulators withdrew the banking license of Bankhaus Herstatt, a mid-sized bank in Cologne, in the middle of the trading day. The timing is what made it famous. Herstatt's FX counterparties had already irrevocably paid the Deutsche Mark legs of that day's trades in Frankfurt. The corresponding dollar legs were due to settle hours later in New York. They never did. Banks that had done nothing wrong except pay first were left holding losses on trades that were half-settled: one leg complete, one leg gone. The episode was significant enough to name a category of risk - settlement risk, still called Herstatt risk - and it pushed the G10 central banks to form the Basel Committee on Banking Supervision later that same year. Here is the part worth sitting with: the actual fix took 28 years. The fix was a central utility CLS launched in 2002 with one job: settle FX trades payment-versus-payment. Both legs of a trade settle simultaneously, or neither does. There is no window in which one side has paid and the other has not. It works - CLS settles on the order of trillions of dollars a day - and it is the reason a Herstatt-style failure has not repeated at scale in the currencies it covers. But look at the shape of the solution. To make two legs atomic, traditional finance built one institution that every major bank trusts, connected the world's main currencies to it, and routed the trades through it. Atomicity was achieved by adding the most systemically important middleman in the history of payments. That was probably the only option available to 1990s banking infrastructure. It is not the only option available now. The agent economy is still in its payments era A study published last week by Keyrock, run with Coinbase and Tempo, put numbers on machine-to-machine commerce: 176 million transactions, $73 million settled between May 2025 and April 2026, average transaction size around $0.31. Those numbers describe a payments economy. A payment is a singl
AI 资讯
When a Hybrid App Button Does Nothing
A mobile user taps a button. Nothing opens. There is no validation message, exception, or visible loading state. The control simply appears dead. These bugs are frustrating because the visible symptom is tiny while the real interaction crosses several technical boundaries. I recently investigated this kind of failure in a Blazor Hybrid image workflow. The feature behaved sensibly in a desktop browser, but the same interaction did not reliably open the photo picker inside an iOS WebView. The useful lesson was broader than the eventual CSS change: A native capability launched from hybrid web UI is a cross-layer contract, not a single component event. To make the interaction dependable, the browser gesture, responsive dialog, native application metadata, and automated tests all had to agree. The visible button was not the real control Styled file-upload controls commonly hide the browser's native file input. A label or custom button receives the click and forwards it to the hidden input. That pattern can work well on desktop browsers. It provides visual freedom while retaining a native file-selection control underneath. The implementation I examined had taken the hiding quite far: the real input was clipped to a tiny area, while a separate visible element acted as its proxy. On desktop, the browser carried the user action through that indirection. Inside the iOS WebView, the picker did not open. This matters because browsers deliberately protect privileged actions. File pickers, cameras, pop-ups, clipboards, and media playback often require a trusted user activation. The further the real privileged element is removed from the original tap, the more likely platform differences become visible. The fix was conceptually simple: make the transparent file input span the visible button. The control still looks custom, but the user's tap now lands directly on the input that owns the privileged action. The input is visually transparent, not functionally absent. One repaired lay
AI 资讯
How I Built the Editor and Compile Pipeline for a TypeScript DSA Visualizer (DSA View View 👀👀)
Hoi hoi! I'm @nyaomaru, a frontend engineer. Thanks to my daily Duolingo streak, Dutch is finally...
AI 资讯
ASCE 7 load combinations without the spreadsheet drift
Structural engineers evaluate ASCE 7 Chapter 2 load combinations on nearly every design. The factors themselves are not hard — the failure mode is a spreadsheet cell that someone “improved” six months ago, or a notebook that only checks one combination. loadcomb is a small, dependency-free Python library and CLI that evaluates the basic LRFD and ASD combination sets on scalar load effects you already have from analysis. pip install loadcomb loadcomb --D 120 --L 80 --S 40 --W 55 --method LRFD from loadcomb import LoadCase , governing g = governing ( LoadCase ( D = 120 , L = 80 , S = 40 , W = 55 ), method = " LRFD " ) print ( g . id , g . value , g . formula ) What it handles Basic LRFD and ASD combinations from ASCE 7 Chapter 2 (Lr or S or R) resolved to the roof variable with largest absolute effect Automatic ± envelope for wind and earthquake Governing combination by absolute value What it is not Not FEA, not member design, not every exception in the standard. Confirm the ASCE 7 edition and local amendments for your project. Links pip install loadcomb GitHub PyPI Blog: sybilgambleyyu.github.io/posts/loadcomb.html MIT licensed.
AI 资讯
visibility modifiers in coluber.
Visibility modifiers is a method used in programming to specify the specific object for it's as visible or invisible, the purpose of visibility modifier is to define what is able to access or what is not to be able to access the object. In coluber it's able to define visibility modifier at several objects: data. task. As an example defined: public data measurement: inch: float type_meas: string public task process(): serve measurements = measurement(inch: 1.5, type_meas: "meter") public, defined as visibility modifier it can accessed through main.clbr at the root project or across modules in stdlib or library. private data measurement: inch: float type_meas: string private task process(): serve measurements = measurement(inch: 1.5, type_meas: "meter") main.clbr or other modules in both are unable to access the objects for as is the private modifier.
AI 资讯
From Variables to Closures
🚀 JavaScript Fundamentals (Week-03): Understanding the Concepts That Every Developer Should Know "Writing JavaScript code is one thing, but understanding what happens behind the scenes is what makes you a better developer." When I first started learning JavaScript, I knew how to declare variables and write functions. However, I often found myself asking questions like: Why are there three ways to declare variables? What exactly is hoisting? How does JavaScript execute my code? Why can an inner function access variables from its parent function? What does the this keyword actually refer to? Why do developers keep talking about writing clean code? This week, I focused on understanding these core JavaScript concepts instead of simply memorizing syntax. In this article, I'll explain each concept in a beginner-friendly way with examples and practical explanations. 📚 Topics Covered Variables ( var , let , const ) Hoisting Lexical Scope Execution Context Call Stack Closures this Binding DRY Principle KISS Principle Let's start from the beginning. 📦 Variables in JavaScript What is a Variable? A variable is a named container used to store data in memory . Instead of writing the same value repeatedly, we store it inside a variable and reuse it whenever required. For example, let name = " Sai " ; console . log ( name ); Output Sai Here, let → Variable declaration keyword name → Variable name "Sai" → Stored value Why Do We Need Variables? Imagine writing this: console . log ( " Sai " ); console . log ( " Sai " ); console . log ( " Sai " ); If the value changes, every occurrence must be updated. Using variables, let name = " Sai " ; console . log ( name ); console . log ( name ); console . log ( name ); Now changing one line updates every usage. Variables improve: Readability Reusability Maintainability Types of Variables JavaScript provides three ways to declare variables. var let const Although all three create variables, they behave differently. var var was introduced in the
AI 资讯
What Changed in Zod 4, and How I Migrated Production Schemas
Headline: Zod 4 is a rewrite of the TypeScript-first schema validation library, released as the stable major in 2025. Four changes hit my code directly: string formats moved to top-level functions ( z.email() instead of z.string().email() ), the four error options collapsed into one error parameter, error formatting moved to standalone helpers ( z.flattenError , z.treeifyError , z.prettifyError ), and .strict() / .passthrough() became z.strictObject() / z.looseObject() . The deprecated Zod 3 APIs still work with warnings, so I migrated incrementally. Key takeaways Zod 4 is the stable major of the TypeScript-first schema validator, released in 2025; it requires TypeScript 5.5 or newer. String formats are now top-level tree-shakeable functions — z.email() , z.uuid() , z.url() — and z.string().email() is deprecated but still works. A single error parameter replaces Zod 3's message , invalid_type_error , required_error , and errorMap . Error formatting moved to z.flattenError (form fields), z.treeifyError (nested), and z.prettifyError (human-readable string). The zod/mini build exposes the same validators through a functional, tree-shakeable API; z.infer , .parse() , and .safeParse() did not change. I reach for Zod on almost every project to validate untrusted input at the boundary — request bodies, form data, environment variables, API responses. Zod 4 changed enough of the surface that a mechanical upgrade tripped a handful of files, so I mapped exactly what moved. What actually changed in Zod 4? Zod 4 is a ground-up rewrite of the TypeScript-first schema validation library, released as the stable major in 2025. The headline is performance: the Zod team's release notes report large reductions in TypeScript compiler instantiations and faster runtime parsing, which matters most in large codebases where schema types dominate type-check time. Four API changes touched my code directly — string formats moved to top-level functions, the four error options collapsed into one,
科技前沿
Samsung Galaxy Unpacked July 2026: Live updates from the Z Fold 8 and Flip 8 launch event
Join us as we bring you live coverage from Samsung's Unpacked event in London, where it's expected to reveal new foldables and wearables.
AI 资讯
Introducing OpenAI Presence
Introducing OpenAI Presence, a proven enterprise AI agent platform that helps organizations deploy trusted voice and chat agents for customer and internal workflows.
AI 资讯
Ebook Reviewer Wanted: Help Me Find What's Gone Stale
Technical books have a shelf life that nobody prints on the cover. A cookbook from 2018 still works. A history book from 2018 still works. A Laravel book from 2018 will teach you a middleware pattern that was replaced twice, recommend a package whose maintainer archived it, and show you a test suite in a syntax that no longer runs. I've written a series of ebooks on PHP, OOP, SOLID, design patterns, Laravel conventions, testing with Pest, application architecture, and AI-assisted development. There are also two aimed at kids learning to code. Every one of them was correct when I wrote it. That is a much weaker claim than "correct now," and I'd rather find the gap myself than have a reader find it for me. So: I'm opening the series to technical reviewers before the final edition ships. Applications close Sunday, 26 July. Why I can't do this alone Not because I lack the time. Because I lack the distance. When you write a technical book, you build a mental model of the reader and then you write to that model for months. The model is always partly wrong, and you are the last person who can see how. You skip a step because it's obvious to you. You keep an example that made sense when you drafted the chapter and no longer matches the surrounding code. You cite a package because it was the right answer for a project you shipped two years ago, and you never checked whether it's still maintained. None of this shows up on a reread. Rereading your own work is mostly pattern-matching against your own memory. You see what you meant, not what you wrote. A reviewer who has never seen the manuscript reads what's actually on the page. That's the whole value, and it's not something more effort on my side can substitute for. What the job actually is You pick one book from the series. You read it properly — not skim it — and you flag what's broken. Four things in particular: Things that are outdated. The framework moved, the syntax changed, there's now a cleaner way to do the same thin
AI 资讯
The future of AI coding isn't better prompts. It's better engineering constraints.
Over the past few months, I've noticed that most discussions around AI coding assistants focus on prompts. People share: .cursorrules AGENTS.md CLAUDE.md long prompt templates custom instructions The assumption is always the same: "If I explain my engineering practices clearly enough, the AI will follow them." For simple projects, that works. For real software projects, it eventually breaks down. The problem isn't intelligence. It's governance. Every AI coding assistant eventually produces something like this: giant functions skipped tests undocumented architectural decisions ignored security practices direct commits inconsistent commit messages missing pull request descriptions Not because the model suddenly became "worse". Because nothing prevents it from taking shortcuts. Exactly like humans. We already solved this problem... for humans. Professional software engineering has never relied on trust. Instead, we built systems that enforce discipline. We don't ask developers to: write tests We fail CI. We don't ask them to: use meaningful commit messages We reject the commit. We don't ask them: not to push directly to production Protected branches make it impossible. Engineering isn't based on trust. It's based on constraints. Yet with AI... ...we went backwards. Instead of constraints, we write instructions. We create increasingly sophisticated prompt files hoping the assistant will remember them. Always write tests. Always document architectural decisions. Use GitHub Flow. Follow OWASP. Keep functions below 40 lines. Never commit directly to main. Those aren't guarantees. They're suggestions. And suggestions are eventually ignored. Rules are not enforcement. Recently I came across an article making a simple observation: Rules without enforcement are just hopes. That sentence stayed with me. It perfectly describes the current state of AI-assisted development. An AI may fully understand your engineering rules. It may even agree with them. But unless something checks
AI 资讯
Voice Agent Turn-Taking: Stop Live AI Calls From Talking Over Users
Voice agents do not usually fail because the model is “not smart enough.” They fail in the awkward half-second where the user pauses, breathes, corrects themselves, or interrupts while the AI is still talking. That tiny moment decides whether the product feels useful or robotic. If your live AI call cuts people off, talks over them, ignores barge-in, or waits so long that users repeat themselves, no prompt will save the experience. The fix is not one magic model. It is a turn-taking system: audio signals, semantic checks, interruption rules, streaming, and metrics that work together. This guide walks through a practical voice agent turn-taking design you can ship in a real product. Why turn-taking is the real voice agent bottleneck Text chat is forgiving. A user types. The model answers. If the response takes two seconds, the user may still wait. Voice is different. Humans expect conversation to move quickly. A delay feels like confusion. An early response feels rude. Talking over the user feels broken. A production voice agent has to answer three questions again and again: Is the user still speaking? Is the user finished enough for the agent to respond? If the user interrupts, should the agent stop, listen, or continue? Most teams start with a simple pipeline: Microphone -> Speech-to-text -> LLM -> Text-to-speech -> Speaker That is enough for a demo. It is not enough for a live workflow where the caller changes their mind, uses filler words, speaks in a noisy room, or interrupts because the AI misunderstood them. The practical goal is not “lowest latency at any cost.” The goal is comfortable turn timing : fast enough to feel alive, patient enough to avoid cutting people off, and interruptible enough to recover when the user takes control. Research signals behind this topic Recent AI platform activity points toward live agents moving from demos into production workflows: Product launches are emphasizing embedded live agents that can see, speak, and operate inside so
AI 资讯
The Hard Part of a Global Birth-Chart Calculator Was Time
A birth-chart form looks simple: ask for a date, time, and place, then calculate. The interface may be simple. The input is not. 1992-11-01 01:30 does not identify one universal instant. It is a wall-clock reading that only becomes meaningful after you resolve the place, the historical time-zone rule, and any daylight-saving transition. If the time is unknown, inventing a convenient default can create chart features that were never supported by the user’s data. I ran into these problems while building AstroZen , a Next.js application that calculates a BaZi Four Pillars chart and a Western natal chart before generating an optional interpretation. The most important architectural decision was this: Calculation is an evidence pipeline. Interpretation is a separate layer. This post explains the calculation pipeline, the failure modes I had to remove, and why “unknown” must remain unknown. 1. A city name is not a coordinate Early prototypes often use a short city list or a default coordinate. That works for a layout demo, but it is not acceptable once location affects the result. Names are ambiguous: Paris can mean France or Texas. Springfield needs a state or region. Country abbreviations come in several forms. A valid city must resolve to both coordinates and a time-zone identifier. AstroZen sends the submitted city to Open-Meteo’s geocoding service, then scores the returned candidates against the requested country and optional region hint. It keeps the following structured result: type ResolvedPlace = { name : string ; region : string | null ; country : string ; countryCode : string ; latitude : number ; longitude : number ; timeZone : string ; // IANA, for example "Europe/Madrid" }; Candidate selection considers exact city-name matches, country matches, an optional region hint, and population as a small tie-breaker. Population never replaces the country check. The more important rule is what happens when resolution fails: if ( ! selected || ! countryMatches ( selecte
AI 资讯
Is Your AI Agent Production-Ready? Define the Bar First
Every team shipping an agent has the same meeting. Someone asks "is it ready?" and the room splits. One person saw a great demo. Another watched it invent a refund policy an hour ago. The argument runs in circles because nobody agreed what "ready" means, so the loudest opinion wins and the agent ships on a vibe. Making an AI agent production-ready is not a moment of confidence. It is a bar you write down before you build, then measure against. This post is about that bar: why agents need a different one than the services you already ship, and how to define it so "is it ready?" becomes a number instead of an argument. Why "production-ready" breaks for agents For a normal service, "production-ready" is settled. Correct output for valid input, handles errors, meets a latency target, has tests and a rollback. You know the shape of done. An agent breaks three of those assumptions at once: It is non-deterministic. The same input can produce different output, so "correct" becomes "acceptably right, often enough." Its failure surface is open-ended. A function fails in ways you enumerated; an agent fails in ways you never imagined, because it composes language, tools, and judgment on the fly. Its worst case is not a 500 error. It is a confident wrong answer that looks right, which is far more expensive than a crash, because a crash at least tells you it failed. So the honest question is not "is the agent correct." It is "is the agent acceptably wrong, safely, within budget, and repeatably enough to trust." That question has four parts, and each is a line on your bar. The four lines of the bar Write these down before you build. If you cannot fill them in, you do not have a spec, you have a wish. Task success. On a fixed set of real tasks , not the happy-path demo, what fraction must the agent complete correctly? Pick the number. 85 percent means one in seven users gets a wrong answer. Acceptable for this job, or fireable? Decide on purpose. Failure acceptability. Not all wron
AI 资讯
从FROST到FROST-SOP:如何用家族治理模型打造你的"AI分身"?
从FROST到FROST-SOP:如何用家族治理模型打造你的"AI分身"? 作者 :神通说 日期 :2026-07-22 主题 :双项目联动 | 周三轮换 阅读时间 :10分钟 开场:一人公司的终极困境 你是否有这样的体验? 每天醒来,脑子里塞满了要做的事: 写推广文章 回复客户咨询 跟进项目进度 整理会议纪要 复盘昨天数据 一个人做公司,听起来很自由。实际上, 自由职业者的时间比上班族更碎片 ——因为所有事情都堆在你一个人身上,没有任何分工。 我一直在思考一个问题: 能不能用AI Agent来"复制"自己? 不是那种"给你一堆提示词让你自己写"的AI工具,而是真正能 自主执行任务、自动汇报结果、帮你分担80%重复工作 的数字分身。 这个想法最终落地成了两个项目: FROST 和 FROST-SOP 。 第一站:FROST的家族治理模型——AI Agent应该像家族一样分工 FROST 的核心理念来自一个观察: 自然界最稳定的组织形式不是公司,而是家族。 家族有清晰的分工: 祖辈定规矩,不亲自下场 父辈协调全局,分配任务 子辈执行具体事务 把这个逻辑映射到 AI Agent,就是 FROST 的家族治理模型: ┌─────────────────────────────────────────────────────┐ │ 👑 君主(你) │ │ └── 发布任务、查看结果,不干预执行 │ │ │ │ 👴 祖辈(主Agent) │ │ └── 常驻、全局编排、拆解任务 │ │ ↓ │ │ 🕵️ 斥候(侦查Agent) │ │ └── 外出狩猎、收集情报、快速验证 │ │ ↓ │ │ ⚔️ 府兵(执行Agent) │ │ └── 领命执行、重复劳动、汇报结果 │ │ ↑ │ │ 📜 长老(监督Agent) │ │ └── 记录过程、沉淀教训、审计合规 │ └─────────────────────────────────────────────────────┘ 关键洞察: 祖辈是唯一"常驻"的,其他都是动态生成、执行完就解散的临时角色。 这个模型解决了一个核心问题: 谁来决定"这件事该派给谁"? 答案是: 祖辈 。它不需要亲自执行,只需要决定"派谁去"和"怎么汇报"。 第二站:用FROST的四个原子,理解Agent的本质 FROST 只有四个核心概念,却能构建出完整的家族治理系统: from frost.core import Store , Agent , skill_set , skill_get # 四个原子: # 1. Store(记忆)—— Agent的工作空间 store = Store () # 2. Skill(能力)—— 纯函数,无状态 def collect_daily_tasks ( context ): """ 收集今日任务 """ tasks = [ " 写推广文章 " , " 回复客户 " , " 跟进项目 " ] context [ " tasks " ] = tasks return context def execute_task ( context ): """ 执行单个任务 """ current_task = context . get ( " current_task " , "" ) context [ " result " ] = f " 已完成: { current_task } " return context # 3. Agent(细胞)—— 包裹记忆和能力 daily_agent = Agent ( " daily_worker " , store , skills = { " collect " : collect_daily_tasks , " execute " : execute_task }) # 4. SOP(宪法)—— 定义执行顺序 result = daily_agent . run ( sop_steps = [ " collect " , " execute " ], initial_context = { " current_task " : " 写推广文章 " } ) print ( result [ " result " ]) # 输出:已完成: 写推广文章 这四个原子教会我们什么? Store 解决"记忆"问题——Agent需要上下文 Skill 解决"能力"问题——每个动作必须是可复用的单元 Agent 解决"封装"问题——把记忆和能力绑定在一起 SOP 解决"秩序"问题——让执行顺序可控可审计 理解了这四个原子,你就理解了 Agent 的本质—— 它不是魔法,是结构化的委托系统 。 第三站:FROST-SOP——把家族治理变成生产系统 FR
AI 资讯
Next.js 16 on Cloudflare Workers: what broke and what didn't
I shipped a Next.js 16 app on Cloudflare Workers via OpenNext. Not a demo. A real product with streaming chat, server components, D1 at the edge, and anonymous user sessions. Here is what broke, what barely worked, and what turned out to be surprisingly fine. The stack Next.js 16.2 (App Router) @opennextjs/cloudflare 1.19 D1 for SQLite at the edge Streaming chat via the AI binding (DeepSeek-V3 through a Workers proxy) React 19 Tailwind CSS 4 No auth wall, no OAuth, no database on the origin The site runs a few thousand sessions a week across ~30 persona pages, blog posts, guides, and learning content. Most pages are statically generated. The chat interaction is server-rendered components with streaming responses. What worked surprisingly well Static generation and ISR Pages, blogs, guides, persona pages — everything that does not need user-specific rendering — runs as static HTML at deploy time. Next.js 16 with generateStaticParams and fetch caching worked without modification. OpenNext handles the Cloudflare output format. The build step produces something Workers can serve. Revalidations are limited to Workers' cache API, but since most content changes at deploy time, I never hit that limit in production. The one caveat: revalidateTag() does not work the same way in a Workers runtime. Tags are Node.js memory constructs, and Workers are stateless. If you depend on tag-based revalidation for content updates, you need to either trigger deploys or accept stale-while-revalidate behavior from the CDN. D1 at the edge D1 was the least surprising part of the stack. SQL queries from Next.js route handlers feel like calling a regular database. Sessions store in D1, messages store in D1, and the latency is low enough that restoring a full chat thread from 30 messages takes under 200ms cold. The only sharp edge: D1 connections count against your Worker's concurrent request limit in development. With Next.js making its own fetch calls for compilation, I hit the D1 connection ce
AI 资讯
The Anthropic-Physical Intelligence rumor roiling AI Twitter
Anthropic and OpenAI's aggressive 2026 acquisition sprees set the stage for a weekend rumor.