今日已更新 261 条资讯 | 累计 33301 条内容
关于我们

标签:#t

找到 15846 篇相关文章

AI 资讯

What to Put in Your CLAUDE.md (and What to Leave Out)

A great CLAUDE.md is not the longest one. It is the one where every line changes what Claude does. The whole skill is knowing what belongs in it — and, just as importantly, what does not. The sections that earn their place Start with a one or two line project description and your stack, with version numbers. Claude infers a lot from your code, but it will not guess that you are on Next.js 15 instead of 14, or which ORM you chose. Then a directory map — not every file, just the top-level layout with a note on what each part holds. After that: the build and test commands, the conventions a formatter does not enforce, and critically, the things not to touch. # Project: Acme Dashboard Next.js 15 (App Router), TypeScript, Drizzle ORM, Vitest. ## Structure src/app/ # routes and pages src/lib/ # shared utilities, db client db/migrations/ # generated - never hand-edit ## Commands Build: npm run build Test: npm run test ## Conventions - API routes return { data, error } - never throw to client - Server components by default ## Do not touch - db/migrations/ is generated. Never edit by hand. Every line in that file would cause a mistake if removed. That is the bar. What to leave out This is where most files go wrong. Two kinds of content waste your budget: Personality instructions. "Act as a senior engineer," "think step by step," "be thorough." These feel productive but change nothing — Claude already does them. General advice that does not prevent a specific mistake is pure noise. Rules a tool already enforces. If you have a formatter or linter, do not restate what it enforces. Wire it into a hook instead, and keep CLAUDE.md for what tools cannot enforce. The one-line test For every line, ask: "If I remove this, will Claude make a mistake?" If yes, keep it. If no, delete it. This single question, applied ruthlessly, is the difference between a file Claude follows and one it ignores. A bloated file buries the rules that matter in noise, so Claude cannot tell which line is the

2026-06-21 原文 →
AI 资讯

What Is CLAUDE.md? A Practical Guide to Configuring Claude Code

If you use Claude Code, there is one file that quietly shapes every session: CLAUDE.md. Most developers either do not have one or have one that works against them. Here is what it actually is, in plain terms. The file Claude reads every session CLAUDE.md is a markdown file that Claude Code reads at the start of every conversation. Think of it as your project's constitution — the source of truth for how your specific repository works. Because Claude reads it every time, you stop re-explaining your stack, your conventions, and your commands on every task. Why it exists Without a CLAUDE.md, every session starts cold. Claude can read your code, but it cannot infer the things that live outside the code: that you are on Next.js 15 and not 14, that a directory is generated and must never be edited, that your team has a particular commit style. You end up explaining these again and again, slightly differently each time, so the output drifts. CLAUDE.md captures that knowledge once, somewhere Claude always sees it. Where it lives, and how to start Put CLAUDE.md in the root of your project. You do not have to write it from a blank page — the /init command analyses your codebase and generates a starter, detecting your build tools, test framework, and existing patterns: $ claude > /init Treat the result as a foundation, not a finished product. The real value comes from refining it as you learn what Claude gets wrong without guidance. What belongs in it A good CLAUDE.md is short and specific: A one-line stack description, with versions — Claude will not guess Next.js 15 over 14 A directory map — the top-level layout and what each part holds The build and test commands The conventions a newcomer could not infer from the code A "do not touch" section — generated files, migrations, protected paths Here is a compact example: # Project: Acme Dashboard Next.js 15 (App Router), TypeScript, Drizzle ORM, Vitest. ## Structure src/app/ # routes and pages db/migrations/ # generated - never h

2026-06-21 原文 →
AI 资讯

The Real Reason Everyone's Fighting About Tailwind CSS v4

The Tailwind CSS4 debate is everywhere right now. And honestly? Most people are arguing about the wrong thing. The real question isn't "inline styles vs. utility classes" — it's about where your styling decisions live and who pays the cognitive cost. Let me break down what's actually happening, with real code, real trade-offs, and a clear take at the end. What Changed in Tailwind CSS v4 Tailwind CSS v4 introduced a major shift: CSS-first configuration. Instead of a tailwind.config.js , you define everything in your CSS file using @theme : /* Before (v3) - tailwind.config.js */ module .exports = { theme : { extend : { colors : { brand : '#6366f1' , } , spacing : { 18: '4.5rem', } } } } /* After (v4) - main.css */ @import "tailwindcss" ; @theme { --color-brand : #6366f1 ; --spacing-18 : 4.5rem ; } This is cleaner for many workflows. But it's not what's causing the drama. The Real Flashpoint: Utility Density in JSX What's actually triggering the discourse is how v4 accelerates a pattern that was already polarizing — components that look like this: // The "inline styles but make it Tailwind" pattern function AlertBanner ({ type , message }) { return ( < div className = { ` flex items-center gap-3 px-4 py-3 rounded-lg border ${ type === ' error ' ? ' bg-red-50 border-red-200 text-red-800 ' : ' bg-blue-50 border-blue-200 text-blue-800 ' } ` } > < span className = "text-sm font-medium" > { message } </ span > </ div > ); } vs. the @apply approach many teams prefer: /* alert.css */ .alert { @apply flex items-center gap-3 px-4 py-3 rounded-lg border; } .alert--error { @apply bg-red-50 border-red-200 text-red-800; } .alert--info { @apply bg-blue-50 border-blue-200 text-blue-800; } // Cleaner component function AlertBanner ({ type , message }) { return ( < div className = { `alert alert-- ${ type } ` } > < span className = "text-sm font-medium" > { message } </ span > </ div > ); } Both work. Neither is objectively wrong. But they encode very different philosophies. The Philos

2026-06-21 原文 →
AI 资讯

Shipping one Flutter codebase to 6 platforms: what I learned building Tuneline

I spent the last several months solo-building Tuneline , a cross-platform media player, from a single Flutter codebase that ships native apps to macOS, Windows, Linux, Android, Google TV, and iOS . No Electron. Here is the stack and a few things that bit me. The stack Flutter 3.38 / Dart 3.10 — one codebase, six targets. media_kit for playback — libmpv on desktop, ExoPlayer on Android. Avoiding per-platform video plugins was the single biggest sanity win. Riverpod for state, Hive for local storage, Dio for HTTP. Node.js + Prisma backend for the cloud-sync layer, so your library, favorites, and settings replicate across devices. GoRouter with a single-route, tab-driven shell so the same layout reflows from a phone to a 10-foot TV UI. Things that bit me TV is its own design language. A 10-foot, focus-based UI is not a big phone. D-pad focus traversal, larger hit targets, and a separate Google TV store listing were all non-trivial. Per-platform video quirks. Desktop (libmpv) and mobile (ExoPlayer) disagree on enough edge cases that a shared abstraction over media_kit earned its keep. Sync is a distributed-systems problem in disguise. "Set up once, never rebuild it" sounds simple until two devices edit the same data offline. Keeping one canonical decoder for both the socket sync-down and the REST pull saved me from a whole class of drift bugs. One codebase is not one design. Window management on desktop, picture-in-picture per platform, and safe-area handling on mobile each needed platform-specific care even with a shared core. The product Tuneline is a bring-your-own-content player, like VLC — you supply your own playlists and it does not host anything. Every viewing feature is free on one device, and the only paid tier is cloud sync plus multi-device. No subscriptions. Site: https://tuneline.app — happy to answer any Flutter or cross-platform questions in the comments.

2026-06-21 原文 →
AI 资讯

Why Claude Code Ignores Your CLAUDE.md (And How to Fix It)

You wrote a detailed CLAUDE.md, and Claude Code still gets things wrong — wrong convention, touches files it should not, ignores rules you clearly wrote down. The cause is almost never that the rules are missing. It is that they are buried. The over-specified file problem CLAUDE.md loads into Claude's context every single session, and performance degrades as that context fills. When the file grows too long, something counterintuitive happens: Claude starts ignoring parts of it. The important rules get lost in the noise, and the genuinely critical instructions sit too deep to reliably influence output. A bloated file does not just waste tokens. It actively makes Claude less reliable, because it cannot tell which of your hundred lines is the one that matters. The trap of good intentions It always starts reasonably: "let me put everything relevant in here." But relevant is a low bar. The file grows until it is impossible to scan, full of duplication, and so noisy that the truly important rules carry no weight. More content felt like more control. It was the opposite. The fix: prune ruthlessly Run every line through one question: "If I remove this, will Claude make a mistake?" If the answer is no, the line is noise — delete it. And if something only matters in a specific situation rather than always, it does not belong in the always-loaded file at all. That is what skills and subdirectory CLAUDE.md files are for — they load on demand, only when relevant. Let Claude fetch what it needs Instead of embedding everything, tell Claude how to pull context when it needs it. Rather than pasting an entire API guide into the file: # Wasteful - embeds the whole file every session: @docs/api-guide.md # Better - Claude reads it only when relevant: For Stripe integration work, read docs/stripe-guide.md The second form costs almost nothing until the moment it is needed. The result A pruned CLAUDE.md is often a third of the length and many times more effective. The rules that matter are

2026-06-21 原文 →
AI 资讯

Closing Chapter 1: From Query to Data

We opened Chapter 1 with a single line, SELECT * FROM users WHERE id = 1 . For that line to leave the client and come back as a result row, the PostgreSQL backend went through five stages. First it decided which processing path the message should take; then the parser and analyzer turned the text into a tree and gave it meaning from the catalog. The rewriter expanded views and injected policies to transform the tree, the planner weighed the possible execution paths by cost and picked the cheapest one, and the executor followed that plan, pulling up one tuple at a time and sending them back to the client. Chapter 1 was a story about how a query is processed . What tree a given SQL becomes, what plan it turns into, in what order it runs. From start to finish, a chain of logical transformations. But what every one of those stages ultimately deals with is data. The executor pulls up tuples, yet where on disk those tuples lie and in what shape, how they come up into memory, Chapter 1 never asked. When the planner judged an index scan cheaper than a sequential scan, it never opened up what that index physically is. Chapter 1 followed only the logical journey of a query, leaving untouched the substance of the data that journey stands on. Chapter 2, Storage & Access Methods, opens up that substance. In what unit data sits on disk (page), where disk and memory meet (buffer manager), where and how a row survives (heap), and how that row is found quickly (B-tree and the specialized indexes). The very tuple the planner weighed by cost and the executor pulled up in Chapter 1, where it actually came from and how it came to be there, is what Chapter 2 reveals. If Chapter 1 was the logical life of a query, Chapter 2 is the physical dwelling of data. We now look at how the data a query reaches for actually lives on disk.

2026-06-21 原文 →
AI 资讯

1.5.3 Join Nodes: NestLoop, HashJoin, MergeJoin

A scan node sits at the leaf of the tree and pulls rows from a single table. A join node sits in the middle and brings together the rows that its two children send up. It takes one row from users , one row from orders , checks whether they belong to the same user, and if they match, emits the combined row. PostgreSQL has three nodes for this one job: NestLoop, HashJoin, and MergeJoin. The reason a single task splits into three nodes is much like the reason scans did. There is more than one way to find matching pairs from two inputs, and which way is cheapest depends on the size of the inputs and the shape of the join condition. Deciding which way is cheapest, by costing the alternatives, was the planner's job in an earlier chapter. This section looks at what those three nodes actually do when they execute. Given the same two tables, the three find matches in completely different ways, and that difference in approach is exactly what tells them apart. How the three nodes route requests All three join nodes are internal nodes with two children. One child is called the outer, the other the inner. All three run on the Volcano model's pull framework: when the parent asks for the next row, the join node takes rows from its two children, builds one matched row, and sends it up. The only difference is the order and manner in which it routes pull requests to its two children. NestLoop pulls the inner from the start all over again for each outer row it receives. HashJoin slurps the inner in one pass to build an index in memory, then takes outer rows one at a time and probes that index. MergeJoin, on the assumption that both sides are sorted in the same order, advances both sides one step at a time in lockstep. NestLoop: rescan the inner for every outer row The simplest method is NestLoop. As the name says, the loops are nested. The outer loop takes one row from the outer; the inner loop scans the inner from beginning to end, looking for inner rows that match that outer row. Wh

2026-06-21 原文 →
AI 资讯

I Fixed the "AI Commit Messages" Problem in 20 Lines of Python

You've probably seen that trending post — "I Asked AI to Write My Commit Messages and It Was Embarrassing." Same. But instead of accepting embarrassing output, I fixed it. Here's the thing: the problem isn't AI writing commit messages. The problem is how you ask it. One clear system prompt + the actual diff = surprisingly good results. The Setup No new packages. No API key. If you have Claude Code , you're already set. #!/usr/bin/env python3 import subprocess SYSTEM = ( " You are a git commit message generator. " " Output ONLY the commit message — no explanation, no markdown, no quotes. " " Follow Conventional Commits: type(scope): subject. " " Types: feat, fix, docs, style, refactor, test, chore. " " Subject: imperative, lowercase, max 72 chars. " ) diff = subprocess . check_output ([ " git " , " diff " , " --staged " ], text = True ) if not diff . strip (): print ( " Nothing staged. Run `git add` first. " ) raise SystemExit ( 1 ) msg = subprocess . check_output ( [ " claude " , " -p " , SYSTEM + " \n\n " + diff ], text = True , ). strip () print ( msg ) That's it. 20 lines. Uses the claude CLI under the hood — no API key, no config, just your existing Claude Code OAuth session. Why It Works The system prompt does the heavy lifting. Three constraints: Output ONLY the commit message — no preamble, no explanation Follow Conventional Commits — feat , fix , chore , etc. max 72 chars — keeps it readable in git log The diff is the context. You're not asking "write a commit message". You're asking "given these exact changes, what happened?" That's a much more answerable question. Usage # No setup needed if you have Claude Code. Just: git add . python /path/to/git_commit.py # → feat(server): add AI commit message generator via Claude CLI Or wire it into a git alias: git config --global alias.ai '!python /path/to/git_commit.py' # git ai The Results Before: update stuff fix bug WIP added the thing After: feat(api): add generate_commit_message tool to MCP server fix(auth): ha

2026-06-21 原文 →
AI 资讯

Gelişmiş Veri İşleme (Python)

Gelişmiş Veri İşleme (Python) Sıralama, Filtreleme ve Arama – Profesyonel Veri Manipülasyonu Rehberi Python’da veri işleme, sadece döngülerden ibaret değildir. Modern Python yaklaşımı; fonksiyonel programlama araçları , yüksek seviyeli built-in fonksiyonlar ve lambda ifadeleri ile daha kısa, daha okunabilir ve daha performanslı çözümler üretmeyi hedefler. Bu bölümde dört kritik alanı derinlemesine inceleyeceğiz: sorted() ile gelişmiş sıralama lambda ile karmaşık veri yapıları üzerinde sıralama filter() ve map() ile fonksiyonel veri dönüşümü any() ve all() ile toplu doğrulama (validation) Her bölümde gerçek dünya senaryoları ve hands-on örnekler olacak. 1. sorted() Fonksiyonu — Gelişmiş Sıralama Motoru 1.1 Temel Yapı sorted ( iterable , key = None , reverse = False ) Parametreler: iterable: Liste, tuple, set vb. key: Sıralama kriteri (fonksiyon) reverse: True → büyükten küçüğe 1.2 Basit Sıralama ```python id="s1" sayilar = [5, 1, 9, 3, 7] sonuc = sorted(sayilar) print(sonuc) --- ## 1.3 Ters Sıralama ```python id="s2" sayilar = [5, 1, 9, 3, 7] print(sorted(sayilar, reverse=True)) 1.4 Tuple Sıralama ```python id="s3" veri = (10, 5, 20, 15) print(sorted(veri)) --- ## 1.5 String Sıralama (ASCII mantığı) ```python id="s4" kelimeler = ["python", "ai", "data", "backend"] print(sorted(kelimeler)) 2. key Parametresi — Sıralamanın Beyni sorted() fonksiyonunun gerçek gücü burada başlar. 2.1 String Uzunluğuna Göre Sıralama ```python id="k1" kelimeler = ["python", "ai", "veri", "makineöğrenmesi"] sonuc = sorted(kelimeler, key=len) print(sonuc) --- ## 2.2 Sayıların Moduna Göre Sıralama ```python id="k2" sayilar = [10, 3, 7, 21, 14, 9] sonuc = sorted(sayilar, key=lambda x: x % 5) print(sonuc) 2.3 Tuple Sıralama (Gerçek Dünya) ```python id="k3" urunler = [ ("Laptop", 45000), ("Mouse", 500), ("Monitör", 12000) ] sonuc = sorted(urunler, key=lambda x: x[1]) print(sonuc) --- ## 2.4 Çok Katmanlı Sıralama Fiyat → sonra isim ```python id="k4" urunler = [ ("Laptop", 45000), ("Mouse", 500),

2026-06-21 原文 →
开发者

1.5 Executor: How Results Come Back

By the time 1.4 ends, the planner has produced one PlannedStmt. Inside it is an execution tree built from Plan nodes, frozen into a form you can follow step by step, something like "go into the primary key index on users, fetch the one matching row, then output that whole row." But that is still only a blueprint. Reading actual pages off disk, picking out the rows that match the condition, handing results back to the caller: none of that has happened yet. The stage that takes that blueprint and produces actual rows is the executor. The difference between the planner and the executor is the difference between deciding and doing. The planner was the stage that weighed "which index, in what order, with what join method" by cost and chose . The executor takes the chosen approach and carries it out as is . There is nothing left to choose. It just runs the nodes baked into the plan tree and pulls rows out of them. To run it, the executor takes the Plan tree it received and turns it into a PlanState tree. The Plan tree is the static blueprint the planner made, and it does not change during execution. But to actually run, each node needs state that changes as execution proceeds: which row it is reading now, whether the hash table is fully built, what tuple it has buffered from a child. So when execution begins, a PlanState tree with the exact same shape as the Plan tree is created. The blueprint Plan tree is left untouched, and the running state lives in that PlanState tree instead. How the executor produces result rows is the heart of the stage. The executor does not build the entire result set at once and stack it up. Instead, it asks the topmost node of the tree for "the next row," and that request travels down the tree to the leaves. When a leaf scan node reads one row from a page and passes it up to its parent, that row climbs up one level at a time through joins and filters until it reaches the top. The top sends that single row to the caller (the client, or the targe

2026-06-21 原文 →
AI 资讯

1.4.10 Planner Hook: When It Fires, How to Use It

Everything from 1.4.1 through 1.4.9 happened inside a single function, standard_planner() . Building paths, costing them, searching for a join order, estimating cardinality from statistics: all of it runs inside that one function. Yet PostgreSQL does not call standard_planner() directly. It puts another function, planner() , one step ahead of it, and has planner() call standard_planner() . And planner() can be made to call some other function instead of standard_planner() . That replacement is what the planner hook enables. When pg_stat_statements measures per-query planning time, or pg_hint_plan rewrites a plan according to hints, it all goes through this hook. Let's look at how PostgreSQL provides a way to observe or change planning behavior without touching a single line of the core, and how external code plugs into it. All planner() does is check the hook The body of planner() is essentially this. if ( planner_hook ) result = ( * planner_hook ) ( parse , query_string , cursorOptions , boundParams ); else result = standard_planner ( parse , query_string , cursorOptions , boundParams ); planner_hook is a global function pointer. Its default value is NULL , in which case standard_planner() is called right away. A plain PostgreSQL build always takes this path: planner_hook is empty, so the incoming query goes straight to standard_planner() . The key here is the type of planner_hook . typedef PlannedStmt * ( * planner_hook_type ) ( Query * parse , const char * query_string , int cursorOptions , ParamListInfo boundParams ); This signature is identical, down to the character, to that of planner() and standard_planner() . It takes the same Query and returns the same PlannedStmt (the execution plan). So external code only has to write a planner function matching this type and store its address in planner_hook . Let's call this function, written by external code to register in planner_hook , a custom planner function. The moment its address is stored, every planning reque

2026-06-21 原文 →
AI 资讯

I built a free system design whiteboard for engineering interviews

I bombed a system design interview last year — not because I didn't know the architecture, but because I spent the first 5 minutes fighting Excalidraw. So I built SystemDesignBoard — a free, keyboard-first whiteboard specifically for system design interviews. What it does You open it, press a key, and start drawing. No account, no onboarding, no drag-from-a-sidebar friction. R → place a Service node C → place a Database/Cache/Queue A → connect two nodes N → open the scratchpad for scale math The features I'm most proud of Animated connectors that show communication type Instead of just drawing arrows, connectors visually encode how services talk: ⇄ sync — paired dashes (request + ACK) ≋ stream — near-solid fast line with glow (continuous pipeline) This matters in interviews — your interviewer can glance at your diagram and immediately understand the communication pattern. Cloud provider badges Tag any node as AWS (EC2, Lambda, RDS, S3), GCP (GKE, Cloud Run, Firestore), or Azure. Each subtype has its own icon. Trade-off logging Right-click any node → Log Trade-offs → attach your CAP theorem stance, consistency level, and scaling strategy directly to the component. Diagram-as-Code Type: [Mobile App] -> [API Gateway] [API Gateway] -> [Auth Service] [Auth Service] -> [Users DB] [Feed Service] -> [Posts DB x3] [Feed Service] -> [Redis Cache] Hit Apply — it auto-lays out the whole architecture in seconds. Export to animated GIF Export your diagram as a GIF that shows live traffic flow animations. Great for sharing after an interview or in a design doc. Tech stack React + TypeScript + Vite @xyflow/react (ReactFlow v12) for the canvas Zustand + Immer for state with full undo/redo html-to-image + gifshot for PNG/GIF export It's free and open No signup required. Works entirely in the browser. Free during beta. 👉 systemdesignboard.com Would love feedback — especially from anyone who's done system design interviews recently. What's missing? What's annoying? Drop a comment below

2026-06-21 原文 →
AI 资讯

PostgreSQL Indexing Deep Dive - Choosing the Right Index

In the earlier posts of this series, we looked at practical query tuning tips and how to read and interpret query plans . A recurring theme in both was: "add an index here." But "add an index" is a bit like saying "use the right tool" — the interesting part is which one. PostgreSQL ships with several index types, each tuned for a different kind of data and query. Picking the wrong one means PostgreSQL quietly ignores your index and goes back to a sequential scan. In this post, we'll walk through the main index types, when each shines, and the special index variations (composite, partial, covering, expression) that often matter more than the type itself. Setting the Scene: Schema and Sample Data We'll reuse the same schema from the previous posts, with one small addition — a metadata JSONB column and a tags array on orders , so we can explore the more exotic index types. CREATE TABLE customers ( id SERIAL PRIMARY KEY , customer_name VARCHAR ( 255 ), email VARCHAR ( 255 ), created_at TIMESTAMPTZ DEFAULT NOW () ); CREATE TABLE orders ( id SERIAL PRIMARY KEY , customer_id INT REFERENCES customers ( id ), order_date TIMESTAMPTZ DEFAULT NOW (), total_amount NUMERIC ( 10 , 2 ), status VARCHAR ( 20 ), tags TEXT [], metadata JSONB ); -- Insert sample customers INSERT INTO customers ( customer_name , email ) SELECT 'Customer ' || i , 'customer' || i || '@example.com' FROM generate_series ( 1 , 1000000 ) AS s ( i ); -- Insert sample orders INSERT INTO orders ( customer_id , order_date , total_amount , status , tags , metadata ) SELECT ( RANDOM () * 1000000 ):: INT , NOW () - interval '1 day' * ( RANDOM () * 365 ):: int , ( RANDOM () * 500 + 20 ), ( ARRAY [ 'pending' , 'shipped' , 'delivered' , 'cancelled' ])[ FLOOR ( RANDOM () * 4 + 1 )], ARRAY [( ARRAY [ 'gift' , 'priority' , 'fragile' , 'bulk' ])[ FLOOR ( RANDOM () * 4 + 1 )]], jsonb_build_object ( 'channel' , ( ARRAY [ 'web' , 'mobile' , 'store' ])[ FLOOR ( RANDOM () * 3 + 1 )]) FROM generate_series ( 1 , 1000000 ) AS s ( i

2026-06-21 原文 →
AI 资讯

The Playwright Playbook — Part 7: The CI/CD Setup Nobody Shows You

The Playwright Playbook — Part 7: The CI/CD Setup Nobody Shows You "A test suite that only runs on your laptop isn't a test suite. It's a hobby." Six parts in, we have a serious framework. POM-based UI tests. Network interception. Multi-user contexts. A full API testing layer. Visual regression across four viewports. A complete debugging toolkit. Now it needs to run automatically. On every pull request. On every merge. On every deployment. Without you touching it. Most CI/CD tutorials for Playwright show you this: # The "tutorial" version everyone copies - run : npx playwright test That's not a CI setup. That's a shell command in a YAML file. A real production CI/CD pipeline for Playwright has: Sharding — split tests across multiple machines and finish in a fraction of the time Browser matrix — Chromium, Firefox, WebKit in parallel Docker — identical environment on every machine, every time Artifacts — HTML report, traces, screenshots, videos — downloadable from every run Failure notifications — your team knows within seconds, not the next morning Separate VRT workflow — visual regression on its own cadence, not blocking every PR Environment-specific pipelines — staging vs production, different configurations Let's build all of it. 🎯 🏗️ Where We Left Off After Part 6, our full project structure is: playwright-playbook/ ├── tests/ │ ├── auth/login.spec.ts ✅ Part 1 │ ├── tasks/task-management.spec.ts ✅ Part 1 │ ├── network/ ✅ Part 2 │ ├── multi-user/ ✅ Part 3 │ ├── multi-tab/ ✅ Part 3 │ ├── api/ ✅ Part 4 │ ├── visual/ ✅ Part 5 │ └── debug/trace-examples.spec.ts ✅ Part 6 ├── pages/ │ ├── LoginPage.ts ✅ Part 1 │ ├── TaskPage.ts ✅ Part 1 │ └── DashboardPage.ts ✅ Part 3 ├── api/ │ ├── TaskApiClient.ts ✅ Part 4 │ └── AuthApiClient.ts ✅ Part 4 ├── fixtures/ │ ├── auth.fixture.ts ✅ Part 1 │ ├── tasks.json ✅ Part 2 │ ├── empty-tasks.json ✅ Part 2 │ ├── tasks-har.har ✅ Part 2 │ ├── multi-user.fixture.ts ✅ Part 3 │ └── api.fixture.ts ✅ Part 4 ├── scripts/ │ └── record-har.ts ✅

2026-06-21 原文 →
开发者

Why I Redesigned StrictBlock to Make Focus Feel Easier

I rebuilt StrictBlock (my app) from the ground up. StrictBlock is an iPhone app blocker and focus app designed to help people stop procrastinating, protect deep work, and build better focus habits. For this relaunch, I did not want to just “refresh the UI.” I wanted to redesign the full product experience around one question: How can I make starting a focus session feel simple, strict, and useful? The new version focuses on reducing friction. Users can create focus profiles for study, work, sleep, deep work, or Pomodoro sessions, then start blocking distracting apps and websites with less setup. I also redesigned the app around accountability. StrictBlock now includes streaks, trophies, weekly reports, widgets, session journaling, and consequences for ending sessions early. As a developer, this redesign was a good reminder that productivity apps are not only about features. They are about behavior. The UI, the flow, the defaults, and the friction all shape whether someone actually stays focused. StrictBlock is now live with a complete redesign. Would love feedback from other builders, iOS devs, and product engineers. Try it here: Download strictblock on appstore

2026-06-21 原文 →
产品设计

Why UPI and Fintech Apps Need Business Logic Testing (Not Just Security Testing)

Most fintech breaches you read about involve a hacker, a vulnerability, and a headline. Most fintech losses I've actually seen up close involve none of those things. They involve someone who read the terms of a cashback offer more carefully than the product team did, found the one path through the workflow nobody had tested, and quietly walked away with money the system handed over willingly. That's the part standard security testing misses. A penetration test asks: can someone break in? Business logic testing asks a more uncomfortable question: what happens if someone uses every feature exactly as designed, just not exactly as intended ? In a country processing billions of UPI transactions a month, that second question matters just as much as the first — arguably more, because nobody needs a zero-day to abuse a referral program. Here's where that gap shows up most often in Indian fintech apps. Wallet Systems: Built for Speed, Tested for Function, Rarely Tested for Abuse A digital wallet sits at the intersection of multiple money-in paths — UPI, card, net banking, cashback credits — and at least one money-out path. Every intersection like that is a place where timing and assumptions can quietly fall apart. The classic version of this is a race condition: top up the wallet and spend from it in two near-simultaneous requests, and check whether the balance check happens before or after both transactions are committed. Done right, this should be impossible. Done wrong, a user can spend money that, technically, hadn't arrived yet — or spend the same balance twice. There's a quieter version of the same problem around refunds. If a refund is credited back to the wallet on a different timeline than the original debit was finalized, there's often a window where the balance briefly shows more than it should, and a fast enough user can act inside that window before reconciliation catches up. And then there's KYC tiering. Minimum-KYC wallets in India are deliberately capped at

2026-06-21 原文 →
AI 资讯

Use Unix Domain Sockets on Windows Python: Building an AF_UNIX Compatibility API

Python provides socket.AF_UNIX , asyncio.open_unix_connection() , and asyncio.start_unix_server() for working with Unix Domain Sockets on Unix-like operating systems. On Windows, however, support for Unix Domain Sockets tends to depend on the Python version and runtime environment. In particular, differences become apparent when trying to use the higher-level asyncio APIs in the same way as on Unix. To address this, I created a compatibility layer that hides the differences between Unix and Windows and allows AF_UNIX sockets to be used through a largely identical API. This article covers two types of APIs: An asyncio -based AF_UNIX compatibility API A synchronous socket -based AF_UNIX compatibility API Goal The objective is straightforward. On Unix, use the standard library APIs as-is. On Windows, fill in the missing functionality so that application code can remain as unified as possible. For example, on Unix you can write: reader , writer = await asyncio . open_unix_connection ( path ) And on the server side: server = await asyncio . start_unix_server ( handle_client , path ) The goal is to preserve this style of programming on Windows as much as possible. What Was Built The compatibility layer consists of two major components. 1. Asyncio Version This is the asynchronous implementation designed to match the asyncio Unix Domain Socket APIs. The main APIs are: await open_unix_connection ( path , * , limit = ...) await start_unix_server ( callback , path , * , limit = ..., backlog = ...) await create_unix_connection ( protocol_factory , path , ...) await create_unix_server ( protocol_factory , path , ...) install () On Unix-like systems, these simply delegate to the standard asyncio implementation. On Windows, they use Winsock AF_UNIX sockets and combine WSAEventSelect with event-loop handle waiting to implement asynchronous operations. 2. Synchronous Socket Version This version provides a traditional blocking-socket-style API without using asyncio . The main APIs ar

2026-06-21 原文 →
AI 资讯

Evaluating Kimi 2.5 vs Kimi 2.6: What happens to agent skills when the model gets smarter?

When a stronger model ships, there are two questions every skill author should want answered, and evals are the only honest way to answer either: Which skills just got absorbed? A model that now knows how to do X natively does not need a skill telling it to do X. Fewer skills to maintain, leaner context, lower cost. Which skills still matter? Behaviour-level guidance (conventions, preferences, project-specific workflows) is not something pretraining will fill in for you. Those skills should keep paying. Moonshot gave us early access to Kimi K2.6. We ran the Tessl agent skill evaluation harness on the same 21 skills and 100 paired scenarios against three solvers: Kimi K2.5, Kimi K2.6, and Claude Sonnet 4.5. A solver is the model whose output the grader scores; a paired scenario is the same task run twice per solver, once without the skill installed and once with it. These are early signals from one pre-release on one skill set. A deeper cross-model analysis with clean baselines across the board is in progress and will be its own piece. What does our setup look like? Scenarios and rubrics are held fixed across the two Moonshot runs. The only variable is the solver. Solver A: Kimi K2.5 Solver B: Kimi K2.6 Scenario generator: Claude Sonnet 4.5, up to 5 scenarios per skill, derived from each skill's SKILL.md Grader: Claude Sonnet 4.5, weighted-checklist rubric derived from the same SKILL.md Per skill × per solver: every scenario solved twice, baseline (no skill installed) and with-skill Per-skill n=5 is noisy; the aggregate over 100 scenarios is where the signal lives. Three findings: Kimi 2.6 is a better model than K2.5: Without skills, K2.6 sits ~2 pp (percentage points) above K2.5 in aggregate, with double-digit moves on specific skills. Kimi 2.6 holds its own against Sonnet 4.5. We picked Sonnet 4.5 as a competitive baseline, and found in this evaluation set that the K2.6 performed better both in the with/without skill scenario by around ~8 p.p . Skills remain a dura

2026-06-21 原文 →