AI 资讯
I Found a Silent Bug in Formbricks That Crashes Live Surveys at Runtime
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview Formbricks is an open-source survey and experience management platform built with Next.js, TypeScript, React, and Tailwind CSS. It lets teams create and deploy surveys across websites, apps, and email. Developers can self-host it or use the cloud version. With over 12,000 GitHub stars and hundreds of contributors, it is one of the most actively maintained open source alternatives to Qualtrics. Bug Fix or Performance Improvement Formbricks supports custom regex validation rules on survey questions. A survey creator can set a pattern that user responses must match before they are accepted. The problem was in how that pattern was stored. The validation schema for regex pattern rules only checked that the input was a non-empty string: // Before the fix export const ZValidationRuleParamsPattern = z . object ({ pattern : z . string (). min ( 1 ), flags : z . string (). optional (), }); z.string().min(1) means "give me any string with at least one character." It does not verify that the string is actually a valid regular expression. So a survey creator could type [invalid as their pattern, the schema would accept it, it would be saved to the database, and then when a real user submitted a response, the system would try to run new RegExp("[invalid") , JavaScript would throw a SyntaxError , and the survey would crash silently at runtime. The bug never surfaced during setup. It only appeared when a real user was trying to submit a real response. Code Pull Request: github.com/Tobore005/formbricks/pull/1 Here is the fix: const isValidRegexPattern = ( pattern : string ): boolean => { try { new RegExp ( pattern ); return true ; } catch { return false ; } }; const isValidRegexFlags = ( flags : string | undefined ): boolean => { if ( flags === undefined ) return true ; try { new RegExp ( "" , flags ); return true ; } catch { return false ; } }; export const ZValidationRuleParamsPa
AI 资讯
Resolviendo los 404 de Google Search Console
Tres semanas después de publicar un sitemap dinámico que orgullosamente listaba cada perfil de miembro, Google Search Console me dijo que esos perfiles eran un error. No con un error de plano, sino con dos veredictos más callados: Soft 404 y Duplicada sin canónica seleccionada por el usuario . Esta es la historia de leer ese reporte, separar el ruido de la única señal real, y el arreglo, que fue quitar páginas del índice, no agregarlas. TL;DR El reporte "Por qué las páginas no se indexan" de Google es ~80% benigno por diseño. Aprende a triarlo o vas a perseguir fantasmas. Mis perfiles de miembros salieron marcados como Soft 404 (uno) y Duplicada sin canónica seleccionada por el usuario (otro). La misma causa raíz: el perfil público anónimo es deliberadamente flaco, un esqueleto con el username, todo lo demás es PII oculta a quien no ha iniciado sesión. Flaco + casi idéntico entre usuarios se lee como "página vacía" y "clúster de duplicados". No puedes arreglar un Soft 404 enriqueciendo una página que por contrato no tienes permitido enriquecer. Así que el arreglo es noindex,follow en la captura, más sacar las URLs del sitemap (un sitemap que lista una URL noindex es una autocontradicción que Google va a señalar). El modelo mental en una línea: una página que a propósito no tiene contenido para los visitantes anónimos no tiene nada que hacer en un índice construido para visitantes anónimos. El reporte que lo empezó todo El reporte de cobertura del índice, ordenado por número de páginas: Razón Fuente Páginas Descubierta, actualmente sin indexar Sistemas de Google 55 Duplicada sin canónica seleccionada por el usuario Sitio web 9 Página alternativa con etiqueta canónica correcta Sitio web 3 Página con redirección Sitio web 2 Rastreada, actualmente sin indexar Sistemas de Google 2 Soft 404 Sitio web 1 Excluida por etiqueta 'noindex' Sitio web 1 No encontrada (404) Sitio web 1 Bloqueada por acceso prohibido (403) Sitio web 1 Noventa y tantas URLs "sin indexar". El instint
AI 资讯
Optimizing RAG at Scale: Chunking, Retrieval, and the Bayesian Search That Cut Latency 40%
Optimizing RAG at Scale: Chunking, Retrieval, and the Bayesian Search That Cut Latency 40% How we moved from "semantic search + hope" to a measured, tunable retrieval pipeline with 95% recall@10 The RAG Reality Check Everyone ships RAG the same way: chunk by 512 tokens, embed with text-embedding-3-small , top-k=5, stuff into context. It works for demos. Then you hit production: Legal contracts: 512 tokens splits clauses mid-sentence API docs: 1000-token chunks drown signal in noise Customer tickets: Conversational context needs overlap, not fixed windows Latency: 500ms embedding + 200ms vector search + 300ms LLM = 1s+ per query We rebuilt our retrieval layer from first principles. Here's what actually moves metrics. Chunking: One Size Fits None # rag/chunking.py from abc import ABC , abstractmethod from dataclasses import dataclass @dataclass class Chunk : text : str metadata : dict token_count : int chunk_id : str class ChunkingStrategy ( ABC ): @abstractmethod def chunk ( self , document : str , metadata : dict ) -> list [ Chunk ]: ... class FixedTokenChunker ( ChunkingStrategy ): """ Baseline. Good for homogeneous content. """ def __init__ ( self , chunk_size = 512 , overlap = 50 ): self . chunk_size = chunk_size self . overlap = overlap class RecursiveChunker ( ChunkingStrategy ): """ Respects structure: markdown headers, code blocks, paragraphs. """ def __init__ ( self , separators = [ " \n ## " , " \n ### " , " \n\n " , " \n " , " " ], chunk_size = 512 ): self . separators = separators self . chunk_size = chunk_size class SemanticChunker ( ChunkingStrategy ): """ Uses embedding similarity to find natural boundaries. """ def __init__ ( self , model = " text-embedding-3-small " , threshold = 0.7 ): self . model = model self . threshold = threshold class AgenticChunker ( ChunkingStrategy ): """ LLM decides boundaries. Expensive but highest quality for complex docs. """ def __init__ ( self , model = " gpt-4o-mini " ): self . model = model Our production config by
AI 资讯
Building Production-Grade LLM Evaluation Pipelines: From Vibes to Metrics
Building Production-Grade LLM Evaluation Pipelines: From Vibes to Metrics How we replaced "looks good to me" with automated evaluation catching 92% of hallucinations before deployment The Problem: Why "Vibe Checks" Fail in Production Three months ago, our team shipped a RAG-based customer support assistant. It worked great in testing — we'd ask it questions, read the answers, and say "yeah, that looks right." Then it hit production. A customer asked about their billing cycle. The assistant confidently cited a policy that didn't exist. Another asked about API rate limits and got numbers from a competitor's documentation. By the time we caught it, 500+ users had seen hallucinated responses. The post-mortem was brutal: we had zero automated evaluation . Our test process was literally "ask 5 questions, read answers, thumbs up." What Production Evaluation Actually Needs Academic benchmarks (MMLU, HellaSwag) don't tell you if your system works for your use case. Production evaluation needs: Domain-specific judges — Your criteria, not generic "helpfulness" Speed — Evaluation must run in CI/CD, not overnight Regression detection — Know immediately when a prompt change breaks things CI/CD integration — Block merges that degrade quality Golden dataset management — Versioned, stratified, growing test cases Architecture: The Evaluation Pipeline ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ ┌──────────────┐ │ Test Cases │────▶│ LLM Under │────▶│ Judge Ensemble │────▶│ Metrics & │ │ (Golden Set)│ │ Test │ │ - Faithfulness │ │ Regression │ └─────────────┘ └──────────────┘ │ - Instruction F. │ │ Detection │ │ - JSON Schema │ └──────┬───────┘ │ - Custom LLM │ ▼ └────────────────────┘ ┌──────────────┐ │ Dashboard/ │ │ PR Comments │ └──────────────┘ Core Abstractions # eval/base.py @dataclass ( frozen = True ) class TestCase : id : str input : dict [ str , Any ] expected : dict [ str , Any ] | None = None tags : list [ str ] = field ( default_factory = list ) # ["edge-case",
AI 资讯
The agent proposes, the human disposes: building a food-safety autopilot on Qwen
When people demo "agents that automate business workflows," the demo usually ends right where the real problem begins: the moment the agent's output touches the real world. A wrong chatbot answer is annoying. A wrong official violation letter to a restaurant, sent under a county's letterhead, is a lawsuit. For the Qwen Cloud hackathon I built Inspection Autopilot, an agent that does the follow-up paperwork for a county food-safety office, on real public data: 1,333 inspections and 3,579 violation records from Clayton County, Georgia. The interesting part is not the agent. It's the governance around it, and the numbers that prove it works. Start with the receipts Before trusting an agent's judgment, test it against reality. We replayed the county's own history: 350 real (inspection, next-inspection) pairs, each triaged live by qwen-plus using only information available at the time. Facilities the agent flagged URGENT went on to fail their next real inspection 67.6% of the time. Facilities it cleared as ROUTINE failed only 21.1%, against a 48.6% base rate. The tiers are not vibes; the future agreed with them. Three design rules 1. Citations are verified in code, not vibes. The triage agent must justify every risk call by citing violation lines copied verbatim from the inspection record. The backend checks every citation against the source; anything that does not match is dropped and counted. On the committed live eval (50 inspections, 28 of them deliberately dangerous cases), the measured hallucination rate is 0.0% across 126 citations. And because a checker that never fires is indistinguishable from one that does not work, we sabotaged it: 50 forged citations injected, half invented outright, half real violation text lifted from different inspections, the forgery a lazy validator would miss. It caught 50 of 50 and preserved all 75 legitimate citations. We know the tripwire works because we set it off. 2. The log is append-only. Proposals and decisions are insert-only
AI 资讯
The QR Code Was Invented in 1994 to Track Car Parts
Scan a menu, pay a bill, onboard a smart plug, and you are using a piece of technology that was never meant for any of those things. The QR code was invented in 1994, and its original job was tracking car parts on a factory floor. Understanding why it was built the way it was explains why it now shows up on nearly every connected device. Who invented the QR code The QR code was created in 1994 by a team led by engineer Masahiro Hara at Denso Wave, a subsidiary of the Toyota group in Japan. At the time, the automotive industry ran on standard one-dimensional barcodes, the striped labels still seen on grocery products. Those barcodes held very little data, usually around 20 characters, and a busy assembly line often needed a worker to scan ten different labels on a single box of components. It was slow, and it was error-prone. Hara wanted a code that could store far more information and be read much faster. His answer was to go two-dimensional: instead of encoding data only in the width of vertical bars, a QR code (short for "Quick Response") stores data across a grid of black and white squares, packing in thousands of characters in a fraction of the footprint. The board game that shaped it The most famous detail of the QR code's origin is where Hara found his design inspiration. He was reportedly playing the board game Go on a lunch break, staring at the black and white stones arranged on the grid, when the idea of encoding information in a two-dimensional matrix of light and dark cells clicked into place. The harder problem was speed. A scanner needs to find the code and figure out its orientation before it can read anything, and in a factory a label might be rotated any which way. Hara's team solved this with the three distinctive square markers in the corners of every QR code. Those "position detection patterns" use a ratio of black to white areas that almost never occurs by chance in printed material, so a scanner can instantly locate the code and work out its an
AI 资讯
Web-Accessibility
Web Accessibility for Startups: 5 Small Wins That Scale Palak jain Palak jain Palak jain Follow Oct 7 '25 Web Accessibility for Startups: 5 Small Wins That Scale # webaccessibility # webdev # frontend # startup 21 reactions 4 comments 4 min read
开发者
This unpronounceable series of glyphs is an incredible side project from Kieran Hebden (aka Four Tet)
Just why? ʅ͡͡͡͡͡͡͡͡͡͡͡(̸̢̛̼̞̭͋ͅ)̸͚̰͛̔̾̀̿͒͂:̴͓̞̑̌̂̆̊͋̀:̸͎̟̯̂̓̌ ҉ ͡ ͞ ͞ ͞ ҉● ࿀ ● ࿀ ● ҉⃝ ⃝͢ ͞ ͘ ͞⃝̕ ͢ ̛ ⃝ ̸ ̡ ͢⃝̧ ͡ ͡ ̀ ̧ ̢⃝͜ ҉ ͞ ͞ ⃝͞ ͞ ͡⃝ ⃝҉҈҉҈҉҈҉҈҉҈҉ :̶̢͙͙͕̠̩͆(̷̮͍͚̫͚͂̍)̵̳̗̊( ̟̞̝̜̙̘̗̖҉̵̴̨̧̢̡̼̻̺̹̳̲̱̰̯̮̭̬̫̪̩̦̥ What am I supposed to do with this? It's un-Googleable. Unpronounceable. It doesn't even render the same way on various platforms. It looks one way on YouTube Music. Another on Apple Music. And yet another on Bandcamp. Even the artist name ⣎⡇ꉺლ༽இ•̛)ྀ◞ […]
开发者
I've Spent 10+ Years in Software Engineering. After Sickness, Burnout, and a Layoff, I'm Rebuilding My Career. Ask Me Anything
Hi, I'm Cesar, a Senior Software Engineer with 10+ years of experience. In 2023, I got sick and...
AI 资讯
How Netflix Built GenPage: a Single GenAI Model to Build Personalized Homepages
GenPage is a generative AI system developed by Netflix to replace its traditional multi-stage recommendation pipeline by directly generating personalized user homepages. GenPage leverages user history and request context as a prompt to produce the entire page, resulting in improved user engagement and reduced serving latency. By Sergio De Simone
AI 资讯
Kodak EC35 is a dirt-cheap point-and-shoot film camera
Following the success of its $99 Kodak-branded Snapic A1, Reto Project is releasing the Kodak EC35, an even more affordable 35mm film camera for just $34.99. The EC35 certainly isn't fancy. Its 25mm acrylic lens with a fixed f/10 aperture and 1/100 shutter speed basically put it on par with a drugstore disposable. The fact […]
AI 资讯
Can an Apple lawsuit derail OpenAI’s hardware plans?
On the latest episode of Equity, we debate whether Apple's lawsuit will cast over OpenAi's much-discussed plans to get into hardware and go public.
AI 资讯
How We Distribute Video Events Across Regions With NATS JetStream
When a new video shows up in one of our regional crawlers, three things need to happen almost immediately: the SQLite FTS5 search index for that region needs a new row, the discovery ranking cache needs to be invalidated, and the sitemap generator needs to know a URL was born. For a long time we did all of this inline, inside the same cron process that fetched the video. It worked until it didn't. A slow FTS5 rebuild would stall the fetch loop, a sitemap write would fail silently, and a crash halfway through meant one region had the video indexed and another didn't. The fetch and the fan-out were fused together, and every failure was a partial failure. The fix was to stop treating "a video was discovered" as a function call and start treating it as an event. At TrendVidStream we run discovery across 8 regions, and the moment we introduced NATS JetStream as the spine between the crawler and the downstream consumers, the whole system got calmer. This post is the concrete version of how we did it: the stream config, the publishers, the consumers, and the mistakes we made that you can skip. Why Not Just Use a Queue Table in SQLite We already had SQLite everywhere, so the obvious move was a jobs table. We tried it. The problems showed up fast: Polling latency vs. load tradeoff. Poll every second and you hammer the DB with mostly-empty SELECT queries across 8 regions. Poll every 30 seconds and your search index lags noticeably behind your crawler. No fan-out. One row, one worker. If the sitemap generator and the FTS5 indexer both need the same event, you either duplicate rows or invent a consumed_by bitmask. Both are ugly. Locking. SQLite's writer lock means the queue table and the actual data table start contending under the multi-region cron bursts we run. Cross-region delivery. Our regions aren't all on the same box. A queue table doesn't cross machines without you building a replication story on top. JetStream solves all four: push-based delivery (no polling), multipl
AI 资讯
On-Premise AI Code Review: How We Deployed Claude Locally in an Air-Gapped Environment (Setup Guide)
The security officer said no. Not "maybe with some modifications." Not "let us review the data handling policy." No. Cloud-hosted AI code review was not going to happen in their environment. The codebase contained classified material. The network was air-gapped. No data leaves the building. End of conversation. This was a defence contractor. Their development team was writing code that couldn't touch any external API, any cloud service, any SaaS platform. But they were also writing 200,000+ lines of code per year with a team that was struggling to maintain review quality across twelve engineers, the exact problem that AI code review solves. The challenge: deploy AI code review capability that runs entirely on-premise, in an air-gapped network, with no external connectivity, while meeting the security and compliance requirements of a classified computing environment. This is the guide we wish existed when we started that project. It covers hardware requirements, model selection, inference server setup, CI/CD integration and the security architecture that made it pass the compliance review. Why air-gapped AI code review is different from everything else Most AI code review guides assume cloud connectivity. The model runs on the provider's infrastructure. The code is sent via API. The review comes back. The security question is about data handling policies, encryption in transit and vendor trust. In an air-gapped environment, none of that applies. The model runs on hardware you control. The code never leaves your network. There is no vendor to trust because there is no vendor in the loop. This sounds simpler from a security perspective. In some ways it is. In other ways it's significantly more complex because you're now responsible for the entire stack, model selection, quantisation, inference hardware, deployment, monitoring, updating and maintaining the system that in a cloud deployment is someone else's problem. Hardware requirements The hardware question is the fir
AI 资讯
What Is a Pointer in C? A Beginner's Guide
What Is a Pointer in C? A Beginner's Guide If you're learning C and pointers are the moment things suddenly feel harder, you're not alone. Pointers trip up more beginners than almost any other concept in the language. But the core idea is simpler than it looks once you strip away the confusing syntax: a pointer is just a variable that stores an address instead of a value. A Variable Normally Stores a Value When you write int age = 25; , C sets aside a small chunk of memory, gives it a label called age , and stores the number 25 inside it. Every variable in your program lives somewhere in memory, and every location in memory has an address, similar to a house having a street address. Most of the time you don't think about that address at all. You just use the variable name and C handles the memory bookkeeping behind the scenes. What a Pointer Actually Stores A pointer is a variable, but instead of holding a regular value like a number or character, it holds the memory address of another variable. Here's what that looks like: int age = 25 ; int * agePointer = & age ; The & symbol means "give me the address of," and * when declaring a variable means "this variable is a pointer." So agePointer doesn't contain 25. It contains the address where 25 is stored. If you want to see the value at that address, you dereference the pointer using * again: printf("%d", *agePointer); would print 25, not the address. Why Not Just Use the Variable Directly? This is the question that trips up most beginners, and it's a fair one. If you already have age , why bother with a pointer to it? The real value of pointers shows up in a few common situations: Passing large data to functions. When you pass a variable to a function in C, it normally gets copied. For a single integer that's cheap, but for a large array or struct, copying is wasteful. Passing a pointer instead means the function works with the original data directly, without duplicating it. Modifying a variable inside a function. Nor
产品设计
Backbeat Forge
Turn drum audio into a score you can actually edit Discussion | Link
AI 资讯
Multi-Agent Interview Coach
This post is my submission for DEV Education Track: Build Multi-Agent Systems with ADK . What I Built Preparing for technical interviews can be overwhelming. I wanted to build a tool that doesn't just give generic questions, but actually analyzes my specific resume to challenge my unique skill set. This led me to build a multi-agent system using the ADK. This multi-agent system, will take user resume and extracts their profile for generating Interview Questions specialized to the candidate profile. The agents communicate in a sequential loop: The Profiler extracts data -> The Interviewer generates questions -> The Judge validates them. If the Judge rejects a question, the Interviewer re-drafts it, ensuring only high-quality, resume-relevant questions make it to the user. Cloud Run Embed Your Agents Profiler Receives a resume PDF GCS path, downloads it in-memory, parses the text content, and builds a summary of skills. Interviewer Reads the candidate summary and drafts 3 technical interview questions designed to test the boundaries of their experience. Analyzes the drafted questions. Passes the iteration if they are resume-specific; rejects/fails them if they are too generic. Key Learnings This project was a fantastic weekend challenge. Working through the Google Codelab gave me a solid grasp of agent-based architectures, specifically implementing Agent, LoopAgent, and SequentialAgent to create a robust workflow. A few key technical takeaways included: Managing Statelessness : Learning to handle agent sessions in a Cloud Run environment was a great lesson in explicit session lifecycle management. Cloud Integration : Integrating Google Cloud Storage for file handling taught me how to bridge in-memory document processing with persistent cloud storage efficiently. Deployment Architecture : Mastering the transition from local development to a containerized, production-ready Cloud Run deployment provided deep insights into modern backend orchestration. Check the Code from
AI 资讯
Does Prisma respect Supabase RLS? No — here's why
Prisma and Drizzle connect as the postgres role and bypass Supabase RLS entirely, so your policies never protect ORM queries. Here's the fix. TL;DR: No. Prisma and Drizzle open their own direct Postgres connection and log in as the postgres role, which owns your tables and carries BYPASSRLS — so Row Level Security is skipped on every ORM query. Point your app's connection at a dedicated, non-owner NOBYPASSRLS role (and keep the auth check in your code), not at postgres . If you built your Supabase project assuming RLS is a safety net on the data itself, adding an ORM quietly punches a hole straight through it. Your policies are still there. They just never run for the ORM's connection. Here's the mechanism, the myth to unlearn, and three fixes in order of how much you should reach for them. Does Prisma respect Supabase RLS? No. RLS is not a global property of the database — Postgres enforces it per role, per statement . A policy only bites for a role that is (a) not the table's owner, (b) has no BYPASSRLS attribute, and (c) is not a superuser. The Supabase JS client satisfies all three because it reaches Postgres through PostgREST, which runs your query as the unprivileged anon or authenticated role. Prisma and Drizzle satisfy none of them: they read DATABASE_URL and open a raw SQL connection as postgres , which owns virtually every table you migrated and holds BYPASSRLS . Either fact alone is enough for Postgres to skip your policies. So the same query that returns one tenant's rows through supabase-js returns every tenant's rows through Prisma. That is not a bug in your policy — it's the connection role. Two doors into the same database There are two completely different paths to your data, and they authenticate as different roles. The supabase-js path (RLS enforced). supabase-js talks HTTP to PostgREST, not to Postgres directly. PostgREST connects as authenticator , validates the request JWT, and does a SET ROLE into anon or authenticated for the statement. Those
AI 资讯
Learning Software Engineering in the Era of AI
Learning software engineering in the past was a straight forward process, you learn the programming language, you build projects in your portfolio, apply to companies, get a job and life goes on. Doing that in the current times might be a little bit different, as when applying for jobs, you can see some new requirements other than your programming skills and portfolio project such as Prompt Engineering, Work with Agents, Claude Code, and others. You may ask yourself, what are those? And if I am new to Software Engineering, will that change my learning path? Lets discuss all this below. Software Engineering in the Past For a long time, the path into software engineering was clear. You picked a language, maybe Java, Python, or JavaScript. You spent a few months learning the syntax, then the fundamentals: data structures, algorithms, how a database works, how the web sends and receives data. After that, you built things such as A todo app, weather app, clone of a website you liked. These projects went into a portfolio, usually a GitHub profile and a simple personal site. Then you applied to companies, passed a technical interview, and started your first job, so the skills you needed were stable, if you learned React in 2018, React was still useful in 2021. Tools changed, frameworks came and went, but the core idea stayed the same: you write the code, you understand what you wrote, and you fix it when it breaks. When Did AI Start Becoming Something Required The shift did not happen in one day. It came in steps. The first step was autocomplete , around 2021, tools like GitHub Copilot started suggesting the next line of code while you typed. Most developers saw it as a nice helper, nothing more. It saved you from writing boilerplate, but you were still the one thinking. The second step was chat , when ChatGPT and Claude became popular, developers started using them to explain errors, review code, and write small functions. Still a helper, but a much stronger one. At this
AI 资讯
Building a Small Terminal Command Helper with an LLM
I regularly lose time to terminal muscle memory. I work across Windows and Unix-like shells, so I will remember the right command in the wrong environment, transpose a Git subcommand, or use a valid binary with an invalid subcommand. The fix is usually easy to find. The interruption is the costly part: stop, search, translate the answer back into the current shell, and try again. The idea is not new. There are projects that help with failed commands, as well as terminal-integrated LLMs such as aichat. However, I wanted something that does one thing well: fix commands in a seamless workflow powered by an LLM. I wanted a deliberately narrow tool: when a command fails, suggest the command I probably meant, let me inspect it, and run it only after confirmation. During a Fable promotion period, I used an LLM coding agent to see how far a well-scoped prompt could get me. With very little steering, it produced a usable Go prototype in roughly an hour. The project is now open source: nudge . The workflow I wanted The core interaction is intentionally small: Run a command as usual. If it fails, type fix (or bare nudge ) to get a suggested correction. Review it, then press Enter to run, e to edit it first, or n to cancel. The cheapest case is a plain typo, which never reaches a model at all: PS> git pshu git: 'pshu' is not a git command. See 'git --help'. PS> fix `git pshu` isn't a valid command. Did you mean: → git push (typo fix for `git pshu`) Run it? [Enter = yes / n = no / e = edit] The (typo fix for ...) label is the tool telling me it answered locally, in under 10 ms, without a network call. The cross-shell version of the same mistake is reaching for a binary that does not exist here. Because the binary is missing, the shell's command-not-found hook fires and I do not have to type anything at all: PS> printenv `printenv` isn't a valid command. Did you mean: → Get-ChildItem env: (list all environment variables) Run it? [Enter = yes / n = no / e = edit] The case that act