AI 资讯
Understanding the Replication Queue in ClickHouse
I was testing out CH-Ops - an admin GUI for self-hosted ClickHouse - on a simple setup: 1 shard, 2 replicas. Stumbled onto the replication queue almost by accident. Here's what I did: I stopped one of the nodes (let's call it Node B), then inserted some data through the other one (Node A). Just wanted to see what would happen. Then, while Node B was still down, I checked it in CH-Ops. It had stuff sitting in its replication queue. My first assumption was: okay, this must be showing what's left to replicate across the cluster - the total pending replication work. So I switched over and checked Node A, the one that was actually up and had just received the insert. Its queue was empty. That didn't match what I expected at all. If the queue was a cluster-wide "here's what still needs to replicate" view, Node A should've shown something too - it was the one that had the fresh data now waiting to reach Node B. Instead it was Node B, the down one, sitting there with pending tasks. That mismatch is what sent me digging. Turns out the queue isn't cluster-wide at all - it's specific to each ClickHouse instance. Once I brought Node B back up, its queue drained in seconds and the data showed up. That whole experiment is basically the entire post in miniature. Here's the mental model I ended up with. A Queue Belongs to a Replica, Not to the Table This is the first thing to get straight. With a ReplicatedMergeTree table, you can have multiple replicas holding copies of the same data. It's tempting to think of replication as one shared pipe between them. It isn't. Each replica keeps its own local replication queue . So if you see: Replica 1 → queue_size = 0 Replica 2 → queue_size = 25 that doesn't mean 25 operations are waiting somewhere in the middle for both replicas to pick up. It means Replica 2, specifically, has 25 tasks it hasn't finished yet. Once that clicked for me, the rest of the system made a lot more sense. So Where Do These Tasks Come From? Replication in ClickHouse
AI 资讯
The same 16 MiB block, fetched once a second for a minute
How do you work on a cut in DaVinci Resolve while traveling, when the footage sits on the studio NAS? KEIBI DROP shows the NAS folder on your laptop, over the internet, and fetches only the parts you touch, so you open the footage on demand straight from Resolve, Final Cut or Premiere. To show that, we recorded a Resolve demo: a NAS in Timisoara serves a shoot, a laptop in Bucharest, 400 km away, edits from it. The first take moved 3.17 GB for 146 MB of clips, and Resolve froze inside a read. By the end of the day four bugs were out. All four had been in the code since July and August. They showed up when an editor's read pattern met a saved contact, a daemon restart, a relayed lane and a Mac that went to sleep. This is what each one was, how the trace found it, and what the same take measures now. Measured, with the conditions on the original page: Import four clips | 2 s | 0.1 MB Build the timeline (filmstrip thumbnails) | 8 s | 199 MB Land on clip 2, play 5 s | 8 s | 32 MB Jump to the 4K clip, play 6 s | 13 s | 78 MB Full version, with the limits and the method: The same 16 MiB block, fetched once a second for a minute Source, MPL-2.0: github.com/KeibiSoft/KeibiDrop
AI 资讯
trelix v3.2.2 to v3.2.5: The Source Tree Was Fine. The Published Package Wasn't.
Run this against the real, published image and watch it fail: docker run --rm --entrypoint trelix-mcp ghcr.io/sairam0424/trelix:3.2.1 --version Exit code 127. Not a crash inside trelix-mcp, not a stack trace, not a permissions error — 127 is the shell's own way of saying the binary you asked for does not exist. And it didn't. The console script trelix-mcp is supposed to install as part of every trelix package was simply absent from the image, on both the slim tag and the -local tag, for the entire life of the 3.2.1 release. Every unit test in the suite was green. Every line of source that builds trelix-mcp was correct. The thing a user would actually get from docker pull did not have the binary its own --version flag implies exists. This article covers four releases — v3.2.2, v3.2.3, v3.2.4, and v3.2.5 — spanning 173 commits and 88 changed files since v3.2.1, which is where the last article in this series left off. That one was about tests that pass without exercising the code they claim to cover: a MagicMock standing in for a real embedder, an all-ones attention mask that makes masked and unmasked math identical, a unit test that asserted a bug as its own specification. This one, on the heels of the mutation-testing push that closed out that arc, is about a different and in some ways more uncomfortable failure mode: tests that pass while exercising the wrong artifact entirely. A green pytest run against src/ says nothing about whether the wheel on PyPI, the image on GHCR, or the binary on the GitHub Releases page actually does what it claims. Those are three separate build products, built by three separate pipelines, and none of trelix's 4,353 collected unit tests had ever touched any of them directly. v3.2.2 through v3.2.4 is the story of finding that gap and closing it with an actual gate, not a promise to be more careful next time. v3.2.5 is a short postscript proving the discipline stuck. The Docker image that shipped without its own server The 127 above wasn't
AI 资讯
A running process is not a ready Minecraft server
A process supervisor can tell you that a process exists. It cannot, by itself, tell you that a Minecraft player can join. I work on ChunkCraft, a Minecraft hosting project. Here is a small state model that helps keep operational status separate from player-facing guidance. Separate three questions Is the process alive? The container or service manager owns this signal. Has the game finished starting? Startup logs or a game-level probe provide this evidence. Can this player join? Client version, edition, whitelist and network reachability still matter. A useful state model is stopped → starting → ready , with failure and unknown states represented explicitly. Avoid converting a failed probe into “stopped”: a timeout means the observation failed, not necessarily that the server died. Tie each state to a next action Observed state Useful guidance Starting Wait for world loading; show recent startup progress Ready Show the complete connection address and expected version Unreachable or unknown Show when the last successful observation happened and offer diagnostics Player rejected Read the actual join error; check version and whitelist The same principle applies to control buttons. A copy-address action is helpful when the address exists and startup has completed. Showing it as the only instruction during startup invites repeated failed joins. Do not confuse observation with proof Even a successful game-level probe does not prove every player can reach the server. Likewise, a positive player-count sample proves someone was connected at that sample time; it does not identify that person or establish uninterrupted availability. Store observation timestamps alongside values. When a collector fails, preserve historical observations but mark them stale. A freshly rendered dashboard is not evidence of fresh underlying data. A small review checklist Does every status describe an observation we actually have? Is an unknown state distinguishable from a confirmed failure? Does th
AI 资讯
I audited 20 design systems for spacing drift. Here is what your team can use from it.
Nobody on your team chose 13px. Someone pasted it. Someone nudged 12px until a border lined up. A coding agent produced it because nothing told it your scale stops at 12 and 16. .card { padding : 13px ; /* off-scale: nearest are 12px or 16px */ margin-bottom : 7px ; /* off-scale: nearest are 4px or 8px */ } Six months later git grep finds forty distinct spacing values, and the design system's spacing page describes a project that no longer exists. This spring I pointed Rhythmguard , the Stylelint plugin I maintain for spacing scales, at twenty public design systems to find out how quiet it could be on code I do not control. The numbers changed the tool more than any feature request has. This is what a team can take from them, whether or not you use this plugin. Part 1. What twenty repositories showed The benchmark clones each repository at a pinned commit, runs the audit, and classifies every finding as real drift or as noise the tool should not have raised. The full table lives in QUIET_BENCHMARK.md and CI regenerates it on every change. A slice: Repo Off-scale findings Scale source Note Mastodon 564 its own --space-* tokens see below Carbon 272 fallback spacing goes through spacing() Primer CSS 97 fallback tokens arrive from a package shadcn/ui 58 its own Tailwind --spacing base Bootstrap 41 fallback spacing goes through $spacer Mantine 30 its own --mantine-spacing-* tokens Radix Themes 7 its own --space-* tokens values written as calc(4px * var(--scaling)) Spectrum CSS 5 fallback everything is a --spectrum-* token Three things held across the set. Drift concentrates in a handful of values Mastodon defines a real spacing scale as custom properties: // app/javascript/styles/mastodon/tokens/_shape.scss --space-3xs : 2px ; --space-xs : 8px ; --space-sm : 12px ; --space-md : 16px ; --space-lg : 20px ; --space-xl : 24px ; --space-4xl : 36px ; --space-5xl : 40px ; Its stylesheets ignore that scale 564 times. Here is the audit's own histogram: ## CSS Off-Scale Values | V
AI 资讯
Batch Processing: From Unix Tools to Distributed Systems
Much of the traditional software operations we deal with are online, we click a button, wait for a moment, and the transaction or operation is completed. But there is a big area that deals with software operations that require offline processing. For example, background processing of jobs, e.g., OpenAI training/improving its existing GPT models behind the scenes using the data it gathers from its users. Batch Processing Whenever such an offline system runs a job that typically generates output from a batch of inputs, we call that batch processing. Inputs here are immutable, which avoids side effects. Benefits of batch processing: You can time travel. In case of any failure or unintentional outputs, you can jump to the last input checkpoint before a batch processing job. This handling is often referred to as human fault tolerance. Using batch processing and offline systems, compute usage efficiency can be improved. For example, whenever a heavy computation needs to be done, it's better to do it in bulk on maybe a GPU compute rather than crashing the CPU host where the server is online. Though the boundary between online and batch processing is not always clear. For example, a long-running database query could also be categorised as batch processing. Another alternative to batch processing is stream processing, which we will understand in the next article. MapReduce MapReduce is a batch processing algorithm that is utilized by Hadoop, CouchDB, and MongoDB as well. It is a balanced approach that is less extreme than completely parallelizing the jobs. There are several other frameworks like this that are now replacing MapReduce. For example, DataFrames APIs, query languages, etc. We will see MapReduce in detail sometime later. Simulating Batch Processing with Unix Tools (Single Host) If you are a Linux user, this simulation could be very easy for you to grasp. If not, just put it in ChatGPT or any AI tool to understand the command in detail if interested. A typical Ngin
AI 资讯
Demystifying HarmonyOS NEXT: A Deep Dive Into the Architecture, ArkUI, and Distributed Core
Under-the-hood breakdown of Huawei’s “Pure HarmonyOS” SDK for engineers and architects. For the past decade, mobile operating system architecture has been dominated by two paradigms: Android’s JVM-based, garbage-collected model, and iOS’s Darwin/Mach kernel with Swift/Objective-C. Huawei’s HarmonyOS NEXT introduces a third path. Often referred to as “Pure HarmonyOS,” this iteration completely drops AOSP (Android Open Source Project) compatibility. It is a microkernel-based, distributed operating system built from the ground up around a custom AOT compiler and a declarative UI framework. If you are a senior engineer or architect, looking at the HarmonyOS SDK can feel disorienting. The terminology shifts from Activities to UIAbilities, from ViewGroups to ArkUI, and from Java/Kotlin to ArkTS. To truly master this ecosystem, we must strip away the IDE abstractions and marketing terminology. Let’s reconstruct the HarmonyOS NEXT SDK from the silicon up — the Feynman way — to understand exactly how the machine breathes. The Core Engine: How Does HarmonyOS Execute Code Without a JVM? Press enter or click to view image in full size Android translates Java/Kotlin into Dalvik bytecode, which runs on the Android Runtime (ART) virtual machine atop a Linux kernel. HarmonyOS NEXT takes a fundamentally different path, utilizing the ArkCompiler and the Ark Runtime. JavaScript and TypeScript are dynamically typed. A virtual machine spends massive amounts of CPU cycle time inferring types and managing garbage collection. This overhead is unacceptable for a high-performance OS UI layer. ArkTS is a strict subset of TypeScript. It explicitly bans any , dynamic property addition, and eval . Why? Because the ArkCompiler is an AOT (Ahead-of-Time) compiler. When you trigger a build in DevEco Studio: 1.The ArkTS code is statically parsed. 2.Because the compiler possesses absolute type certainty (due to strict typing), it translates ArkTS directly into C/C++ data structures. 3.These structures
AI 资讯
The Transactional Outbox Pattern: Dual-Write Consistency in Distributed Systems
The Transactional Outbox Pattern: Dual-Write Consistency in Distributed Systems One of the most dangerous anti-patterns in microservices architecture is the Dual-Write Vulnerability : updating a database record and immediately publishing an event to a message broker (e.g., RabbitMQ, Kafka) in the same API call. If the network fails or the broker is unavailable after the database transaction commits, the event is lost forever. Conversely, if the event publishes but the database rollback triggers, downstream consumers process a phantom event that does not exist in the source of truth. In this deep dive, we architect the Transactional Outbox Pattern with Change Data Capture (CDC) to guarantee At-Least-Once delivery with zero distributed locking overhead. Technical & Interview Cheat Sheet Approach Consistency Guarantee Failure Mode Overhead Dual Write (Naive) None (Eventual inconsistency) Message lost if broker drops Low 2-Phase Commit (2PC / XA) Strict Atomicity Blocking locks, single point of failure Very High Transactional Outbox (Polling) At-Least-Once Polling query table contention Moderate Outbox + CDC (Debezium) At-Least-Once (Zero Table Locking) Requires WAL decoder plugin Optimal 1: Database Schema Design The business entity change and the outbox event MUST commit within the exact same database transaction: -- Business Entity CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid (), customer_id UUID NOT NULL , total_amount NUMERIC ( 12 , 2 ) NOT NULL , status VARCHAR ( 32 ) NOT NULL , created_at TIMESTAMPTZ NOT NULL DEFAULT NOW () ); -- Transactional Outbox Table CREATE TABLE outbox_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid (), aggregate_type VARCHAR ( 64 ) NOT NULL , aggregate_id VARCHAR ( 64 ) NOT NULL , event_type VARCHAR ( 64 ) NOT NULL , payload JSONB NOT NULL , created_at TIMESTAMPTZ NOT NULL DEFAULT NOW () ); -- Index for high-throughput CDC streaming CREATE INDEX idx_outbox_created ON outbox_events ( created_at ); 2: Atomic C# Tra
AI 资讯
Airbnb Cuts Authentication Code by 60% with Server Driven Architecture
Airbnb redesigned its authentication architecture around server driven flows and policy based challenge selection. The new Flexible Authentication system reduced authentication related code by 60%, cut the web client bundle by 100 KB, improved successful authentication by 2.6%, reduced duplicate account creation by 27%, and lowered OTP costs by 11%. By Leela Kumili
AI 资讯
Four Ways Your Background Job Disappears (And How to Stop Each One)
Hello, I'm Maneshwar, and I'm building LiveReview — a blast-radius aware AI code review built for...
AI 资讯
Building a multi-region routing system with Cloudflare Workers
We serve customers primarily in Australia, but we are now expanding to the USA. The timeline for launch is less than 2 months. This is now a race against time to design a multi-region routing system that fits all of our needs. Here is the story. Background Almost all of our customers were based in Oceania. We run our Kubernetes Cluster on GCP in Australia. Go microservices, federated GraphQL, gRPC services. 2 products - Tutoring and Schools. All designed for Australia. Then we expanded to the USA, which meant a new Kubernetes Cluster in US Central. The latency for serving US customers from Australia is an extra 200ms-300ms depending on network conditions - unacceptable. This would mean sharding the data by region, or does it? There are definitely ways to keep a unified dataset even across regions - though we did not need to do so. More on this later. What are the requirements If the only requirements were "Americans get served from America", we wouldn't be here discussing this, would we? Logged in users are served from their own region, wherever they happen to be in the world. Logged out users are routed geographically, as we have no other information to infer their actual region. Account Managers and Admins should be able to access both regions from one button, with a single account. Teaching materials opened via links from the Schools product must be shareable across both regions. Geography takes care of the logged out user, but nothing else. Using geography for a logged in user can be actively wrong. They might be travelling or simply using a VPN. Then comes the Admin; we have a lot of admin operations regarding curricula, which will be entirely separate for both clusters. Account Managers need to be able to see and modify information on both clusters. One admin should be able to access both clusters with a single account. We considered showing data of both clusters on one screen, but ruled it out as it may become too ambiguous or confusing, not worth the technic
AI 资讯
CQRS: Read-Write Separation Design Pattern
In traditional software architectures, we almost instinctively reach for the CRUD (Create, Read, Update, Delete) paradigm. We design an entity model, map it to a relational schema using an ORM, and use that identical abstraction to both alter state and display data on user dashboards. For simple applications, this works flawlessly. But as systems scale—both in business complexity and throughput, this dual-purpose model starts showing fractures: Write logic demands tight validation, transactional boundaries, normalization, and domain invariants. Read logic demands flat, pre-aggregated, denormalized representations across dozens of tables to serve responsive UIs. Trying to satisfy both masters with a single schema leads to unwieldy SQL joins, lock contention, compromised domain boundaries, and performance gridlock. This is where Command Query Responsibility Segregation (CQRS) enters the picture. 1. What is CQRS? Coined by Greg Young and based on Bertrand Meyer’s Command-Query Separation (CQS) principle, CQRS states that an application should use separate models to update and read data . At its philosophical core: Command (Write): Represents an intent to alter domain state (e.g., SubmitOrder , DeactivateUser , ChangeBillingAddress ). A command should focus entirely on domain logic, data integrity, and business rules. In strict CQRS, commands do not return domain data — only an acknowledgment, validation failure, or generated entity ID. Query (Read): Retrieves data without mutating application state (e.g., GetOrderSummaryById , ListCustomerInvoices ). Queries should execute side-effect-free operations that return lightweight Data Transfer Objects (DTOs). ┌────────────────────────────────────────────────────────┐ │ Client │ └─────────────┬────────────────────────────▲─────────────┘ │ │ Execute Command Run Query │ │ ▼ │ ┌───────────────────────────┐ ┌───────────┴─────────────┐ │ Command Model │ │ Query Model │ │ (Validation & Invariants) │ │ (Optimized for DTOs) │ └──────
AI 资讯
Designing a Webhook Delivery System for 10 Million Events a Day
Hello, I'm Maneshwar, and I'm building LiveReview — a blast-radius aware AI code review built for...
AI 资讯
File System Management in Dart
It's a good time to also investigate a bit the dart I/O interfaces, especially the one related to files. The dart:io package must be imported to deal with files and directories in Dart. import 'dart:io' ; A file in Dart is an object instantiated by the File class . This object can be created by simply invoking the default constructor , where its argument will be a String . // default constructor can be used // to create a new object pointing to // a local file. File myFile = File ( "./file1.test" ); fromRawPath() is another constructor, it opens a file based on an List<Uint8> ( Uint8List ), this kind of data is usually generated by utf8.encode or ascii.encode . // fromRawPath constructor can be used // to open a file based on a raw path, // an Uint8list. File myFile2 = File . fromRawPath ( ascii . encode ( "./file2.test" ); ); Finally, the fromUri() constructor will open a file based on an Uri object. // fromUri is another construct that // can be used to open a file based on // an Uri. File myFile3 = File . fromUri ( Uri . file ( "./file3.test" ) ); Now the file object has been created, many attributes and methods are available to control it. Let check first the attributes. In the previous examples, all objects are using a relative path, when the object is returned, the absolute path attribute is set. It is the absolute path representation of the data previously passed. print ( myFile1 . absolute ); print ( myFile2 . absolute ); print ( myFile3 . absolute ); $ dart run File: '/home/user/tmp/cboring/./file1.test' File: '/home/user/tmp/cboring/./file2.test' File: '/home/user/tmp/cboring/file3.test' The original path passed as first argument can be retrieved with the path attribute . print ( myFile1 . path ); print ( myFile2 . path ); print ( myFile3 . path ); $ dart run ./file1.test ./file2.test file3.test The file object can also returns an Uri object with the help of the uri attribute . // this is a closure helper to show // the properties from an Uri object and //
AI 资讯
The wait queue is just a channel: building a small distributed lock server in Go
Sooner or later you hit the same small problem: two services, on two machines, want to touch the same thing at the same moment — append to a shared file, update a row nobody is fencing, call an API that tolerates one caller at a time. One of them has to wait. The usual answers feel heavier than the problem. Put a service in front and serialize everything through it — now you are building a queue, and then a second queue to hand results back, because you no longer know the outcome at the moment you asked. Cache the resource in Redis and lock there — fine until the resource does not fit in memory, and you have inherited Redlock's ordering guarantees (there are none) and its debates. I wanted the lock as its own primitive: lock a key, do the work, unlock the key. Nothing else. That is Locking-Center — a single binary, one dependency, no config file, no consensus layer to operate. This post is about the three ideas that made it small enough to be worth trusting. 1. One channel per key — and the queue comes for free Every key gets a Go channel with a buffer of exactly one: type Channel struct { key string mutexChan chan bool // buffered, capacity 1 } func NewChannel ( key string ) * Channel { return & Channel { key : key , mutexChan : make ( chan bool , 1 )} } Sending into it acquires the lock. Receiving from it releases : c . mutexChan <- true // acquire — blocks if someone already holds the key // ... critical section ... <- c . mutexChan // release The buffer of one is the whole trick. The first send fills the buffer and returns immediately: that caller holds the key. The second send has nowhere to go, so it blocks — and so does the third, and the fourth. The blocked senders are the wait queue. When the holder releases (a receive frees the slot), the runtime wakes the next blocked sender. And it wakes them in order. The Go runtime keeps a FIFO wait queue behind every channel, so callers are served roughly in arrival order rather than whoever happens to reschedule firs
AI 资讯
Very Basic Docker Commands Cheat Sheet
If you ever needed a quick list of Docker commands, here you go.. 1. Check that Docker is installed docker --version Shows the installed Docker version. 2. Run your first container docker run hello-world Pulls the official test image (if needed) and runs it. You should see a “Hello from Docker!” message. 3. See running containers docker ps Lists containers that are currently running. Use docker ps -a to also show stopped ones. 4. See downloaded images docker images Shows every image on your machine (name, tag, size, ID). 5. Stop a running container docker stop CONTAINER_ID Gracefully stops a container. Get the ID from docker ps . 6. Remove a stopped container docker rm CONTAINER_ID Deletes a container that is already stopped. 7. Force stop and remove docker rm -f CONTAINER_ID Force-stops the container (if it’s still running) and removes it in one step.
AI 资讯
Model Predictive Control for Real-Time Robot Navigation
Model Predictive Control for Real-Time Robot Navigation A path planner tells a robot where it should go. A controller determines how the robot should move to follow that path. Model Predictive Control (MPC) repeatedly predicts future behavior and chooses control inputs that optimize a short horizon. MPC Concept Current State | v Predict future states | v Optimize control sequence | v Apply first control | v Measure new state | +----> Repeat The key idea is that the entire control sequence is not executed at once. Only the first action is applied before the problem is solved again. Robot Model For a simple differential-drive robot: x_dot = v cos(theta) y_dot = v sin(theta) theta_dot = omega The controller can predict where the robot will be after applying candidate velocity commands. Optimization Objective A typical objective might penalize: Distance from reference path Heading error Excessive control effort Rapid control changes Collision proximity Conceptually: Cost = tracking_error + control_effort + smoothness_penalty + obstacle_penalty Prediction Horizon Suppose the controller predicts: t0 -> t1 -> t2 -> t3 -> t4 For each candidate control sequence it estimates the resulting trajectory. The optimizer selects the best feasible sequence. Obstacle Handling A cost function can strongly penalize trajectories near obstacles: Obstacle ### ##### ### \ predicted trajectories \---- safe \--- unsafe Hard constraints can also be used when collision avoidance must be guaranteed by the optimization formulation. ROS 2 Architecture /global_plan | v /mpc ^ | /odom /imu /local_costmap | v /cmd_vel Real-Time Requirements MPC is computationally heavier than simple feedback controllers. Monitor: Optimization time Control frequency Solver failures CPU utilization Prediction horizon Sensor latency If optimization misses its deadline, the system needs a safe fallback. Practical Implementation Strategy Start simple: Define a robot model. Implement trajectory prediction. Define tracking
AI 资讯
Podcast: Scott Jenson on Evolving Desktop OS, Local-First, & Agentic UX
In this episode, Scott Jenson, a veteran UX designer known for his work on the Macintosh, Google Maps, and Chrome examines the long-term stagnation of desktop operating systems and the limitations of current mobile and cloud-centric models. By Scott Jenson
AI 资讯
Can AI Actually Understand Design Systems, or Is It Just Guessing the Tokens?
We use AI daily to scaffold code, write copy, and debug layouts, but when it comes to maintaining a strict design system, things get blurry. You can feed an LLM your component library rules, spacing scales, and color tokens, but it still loves to hallucinate random padding values or invent arbitrary classes if you aren't paying close attention. It's great for writing boilerplate, but bridging the gap between a strict visual token structure and AI-generated code often feels like managing a junior dev who ignores the style guide. How are you integrating AI into your workflow without letting it compromise your design tokens and codebase consistency? Do you use it mainly for initial scaffolding, or have you found a reliable way to keep it strictly aligned with your system? If you're into clean design systems, frontend code, and bridging the visual-to-code gap, check out my work at Joemetry.
AI 资讯
Well-Architected Framework Relied On Knowing The Call Graph. But Agents Are Not As Predictable.
For over a decade, we religiously used the well architected framework (WAF) in design reviews. Objective assessment with clear guidance from WAF made our designs risk free (or risk managed) with ambiguities and gaps called out. With agentic AI, there is always a little extra ambiguity. The rhythm of WAF does not strictly match one particular assumption underneath agentic AI: that we can diagram the execution path before the request arrives. All new ambiguities generally stem from this one root. I’ve run design reviews for more than a decade now. Amazon retail first, then AWS, then my own startup, and now healthcare. The rhythm never varied much. Scrutinize the design against the WAF pillars, weigh it against the alternatives, name the gaps and the risks and the open questions, turn the trade-offs into decisions, then move and manage what’s left in the risks. Whether we follow AWS’s six pillars, Google’s, or Microsoft’s four, the content is close enough that the muscle memory transfers. We are answering one question in six different registers: is this system built well enough to trust? And every design I reviewed in those years shared a property so basic that stating it sounds silly. We could enforce the execution path in advance. A single user request might fan out across dozens of services (or a few hundred in retail), queues, and databases, but an engineer could still draw the expected sequence diagram, project TPS for every service, point out the failure modes (and single points of failure), and estimate how much system stress one request would generate. Our early ML workloads (or traditional ML) fit that mold too. Request comes in, features go into a model, inference comes out, and the surrounding application decides what happens next. The model was a component with a latency budget, not a decision-maker. What actually broke, or started to smell Let me be precise about what did not break, because this gets muddled and debated constantly. The wire protocols are f