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

今日精选

HOT

最新资讯

共 29863 篇
第 285/1494 页
AI 资讯 Dev.to

Test Result Reporting and Failing Fast in CI Pipelines

A test failure that takes 20 minutes to surface, buries the error in 3000 lines of log output, and gives no context about what changed is nearly useless. Good test reporting transforms raw pass/fail data into actionable signals. Failing fast — stopping the pipeline the moment you have enough information to make a decision — keeps feedback loops tight and respects developer time. These two concerns are deeply connected: you can only fail fast confidently when your reporting is good enough that a fast failure still gives you everything you need to fix the problem. What Good Test Reporting Looks Like Before discussing implementation, it's worth being precise about what "good" means here: Immediate visibility — failures are surfaced at the PR/commit level, not buried in logs Failure context — what failed, with what input, producing what output, and in which file/line Historical comparison — is this a new failure or a pre-existing one? Trend data — is this test getting flakier? Is the suite getting slower? Actionability — the report points to a fix, not just a symptom Most teams get #1 and stop. The teams that nail all five have fundamentally different debugging velocity. JUnit XML: The Universal Format JUnit XML is the lingua franca of CI test reporting. Almost every test framework can emit it, and almost every CI platform can ingest it. Understanding the format helps you produce better reports. <?xml version="1.0" encoding="UTF-8"?> <testsuites name= "My Test Suite" tests= "42" failures= "2" errors= "0" time= "8.432" > <testsuite name= "UserService" tests= "15" failures= "1" time= "2.1" > <testcase name= "should create user with valid email" classname= "UserService" time= "0.234" > <!-- Empty = passed --> </testcase> <testcase name= "should reject duplicate email" classname= "UserService" time= "0.089" > <failure message= "Expected 409, got 200" type= "AssertionError" > Expected status code 409 but received 200 Request: POST /api/users Body: {"email": "existing@example

Help Me test 2026-07-25 23:52 14 原文
AI 资讯 Dev.to

What if MCP could manage your entire development runtime?

I created Agent-Up , an open-source desktop app and local server for running multiple coding-agent environments on one machine. Worktrees isolate source code, not the runtime The problem is that Git worktrees isolate source code, but they do not isolate the running application. When several agents work on the same monorepo, each one may need its own: application processes, ports, Docker services, logs, runtime state. Without a shared runtime manager, agents end up coordinating those details through shell commands. That is fragile. One agent may reuse a port that another process still owns. A restart may leave an old process alive. Docker services may overlap. Runtime isolation per workspace Agent-Up manages those concerns per workspace. Each workspace gets its own process lifecycle, allocated ports, Docker services, logs, and runtime state. The desktop app also provides one browser session per workspace for reviewing its web applications. Agents control Agent-Up through MCP The current MCP interface supports: starting and stopping workspaces listing registered workspaces reading workspace status The Agent-Up server owns the runtime state behind those operations. That means the agent does not need to independently discover ports, track process IDs, or reconstruct the application topology through shell commands. The missing runtime layer for parallel coding agents This is relevant because current coding agents are increasingly used in parallel. The source-code side of that workflow is already well served by Git branches and worktrees. The runtime side is not. Agent-Up is intended to provide that missing runtime layer. Planned MCP functionality Planned MCP functionality includes: browser inspection and interaction, diagnostics, screenshots, health checks, Playwright flow export. Same workflow, more control Git still owns branches, commits, pull requests, and merges. Agent-Up just owns the local runtime around them. Agent-Up is open source View Agent-Up on GitHub Read t

Daniel Maß 2026-07-25 23:49 14 原文
AI 资讯 Dev.to

Building an MCP server in Python (and connecting it to Claude Code)

An MCP server is a small app that extends an AI model's capabilities by giving it access to custom tools, a particular set of data or workflows. It's based on the Model Context Protocol, which is an open standard for connecting AI apps with these external sources. The most straightforward way to create an MCP server is to use the official SDK, implement a single function and mark it as a tool and then expose it through stdio (standard input/output) which you can register in Claude Code; it basically boils down to a single Python file with a single tool and connecting it end-to-end took us around 10 minutes. Background Generally, the Model Context Protocol defines two sides: The server — it's the app you write that you use to publish tools/data The client — for example Claude Code; it finds and calls available tools based on your permission As for the main purpose of the Model Context Protocol — before it was introduced, every AI app needed its own custom integration with every tool; the Model Context Protocol replaces this with a single standard connector, so to say it's like USB-C for the AI world — you have a single standardised port instead of having to use a separate cable with every device. In terms of the protocol, a tool is just a function that the model can decide to call. So if you want to build an MCP server, you do it when you want your model to have access to some resources you have (like your internal API or database for example) which aren't available through any of the already-published servers. Let's have a look at a minimal example of what such server might look like — a single Python file with a single tool that returns the number of words, characters and lines in the input text. Scaffold the project To set up the project we used uv (a CLI for managing Python projects) and installed the official SDK: uv init word-count-mcp cd word-count-mcp uv add "mcp[cli]" uv init word-count-mcp — initialises a new project called "word-count-mcp" with an uv proje

dsplce.co 2026-07-25 23:46 11 原文
AI 资讯 Dev.to

How I Processed 666K Pages of Flattened PDFs into a Full Text Search Engine

In 2017 the National Archives and Records Administration (NARA) released the JFK files in an unsearchable manner 🔍. I tried doing manual research 🕵🏻. I relied on their provided CSV file of metadata to look for relevant documents to discover something - but I was looking for a needle in the haystack. I didn't know where to begin - but at the very least, I wanted to be able to search the contents therein. At least the National Archives allowed me to bulk download the PDFs. From that, I was able to birth the Apario Writer . In 2020, I began with rails new phoenixvault 🐦‍🔥 and I proceeded on a Zoom call with DJ Nicke - a former animator at Disney - to watch me build the proof of concept of the crowd sourcing declass utility that I envisioned. You see, when I was 7 years old, I had a dream after watching a space focused science program on TV that involved me sitting at the home computer, but interacting with an advanced interface that would help me uncover the mysteries of the day and time of the era. In Stargate SG-1, this concept was explored with the Tolan where Nareem was shocked to discover what Teal'c found in the records buried within a full text interface. Connecting it back to the JFK files released by NARA, they were unsearchable. Agenda on why aside, what could I do about it? This proof of concept grew into a SaaS platform that cost me $7,000 per month to operate over 12 bare meta servers in a private cloud using ESXi. This interface worked, but it was going to be replaced by a cost saving solution architected from the ground up in Go to reduce the dependency graph of the SaaS solution down to a single binary . In order to do this, I needed to create a pipeline. Looking at the SaaS model, I had a series of sidekiq jobs that compiled the assets. In order to improve the performance of that process, running off from Ruby code, I needed to build a new binary from the ground up using Go. I took the course on YouTube from Matt Holiday called Programming In Go and wa

Andrei Merlescu 2026-07-25 23:35 6 原文
AI 资讯 Dev.to

Stop Asking AI Coding Agents to Fix Vague Bugs

A coding agent can produce a confident patch for the wrong problem when the input is only: The upload sometimes fails. Please fix it. That sentence does not identify the smallest failing input, exact error, environment, expected result, frequency, or even whether the reporter reproduced it personally. If the first instruction is “fix it”, the agent has room to turn a suspected cause into a fictional fact. The safer sequence is: preserve the observed failure; record the real environment; state expected versus actual behaviour; reduce the failing case; prove whether it repeats; diagnose and repair only after that evidence exists. 1. Preserve the observed failure Capture the exact error, status code, incorrect output, affected route or command, timestamp, and smallest known input. Remove secrets and personal data before putting logs into an agent context. If the report came from another person and you have not reproduced it, label it as second-hand rather than silently upgrading it to fact. Weak: CSV imports are broken. Useful: At 14:22 UTC, POST /imports returned HTTP 500 for minimal.csv. Response: "column index out of range". The same account can import one-column.csv successfully. 2. Record the environment you can prove Inspect rather than guess: repository and commit; runtime version; operating system or container image; package lockfile; relevant feature flags; local, test, staging, or production target. Do not infer production configuration from your laptop. Environment differences are often part of the bug. 3. Separate expected and actual behaviour Write two observable statements: Expected: POST /imports accepts the smallest valid two-column CSV and returns HTTP 201. Actual: The same fixture returns HTTP 500 with "column index out of range". Neither statement should include the suspected root cause. “Expected: parser handles the off-by-one bug” already assumes the diagnosis. You have not earned that conclusion yet. 4. Reduce the reproduction Start with the repor

skyestrela 2026-07-25 23:34 6 原文
AI 资讯 Dev.to

Two coding agents editing the same issue, no merge conflict. Here is how git refs make that work

Run two AI coding agents on the same repo and the first thing that breaks is not the code. It is coordination. Agent A starts refactoring auth. Agent B, running in parallel, has no idea and starts the same thing. Neither remembers what it did last session, because each one boots fresh with an empty context window. The usual fixes are worse than the problem: a state file in the repo pollutes every diff and conflicts on merge, and an external issue tracker means API tokens, rate limits, and a hard dependency on the network for something that should be local. So I built grite : an issue tracker that lives inside your git repository as an append-only event log, with deterministic CRDT merging so two writers never conflict. No server. No database. No merge conflicts. Just git. The core idea: issues are events, git refs are the log Grite does not store issues as files in your working tree. It stores them as an append-only write-ahead log inside a git ref, refs/grite/wal . Every action, a create, a comment, a label change, is one immutable CBOR-encoded event appended to that log. Your working tree stays completely clean. The only tracked file grite ever writes is AGENTS.md , and that is on purpose, so agents discover the tool automatically. Because the state lives in a git ref, it travels with your code. It branches when you branch. It merges when you merge. It syncs when you git push . If you can push to a remote, you can sync issues. There is no new account, no new infrastructure, no new protocol to learn. How it works Three layers, cleanly separated. The git WAL is the source of truth. Events are appended as CBOR chunks, each identified by a content-addressed EventId that is a BLAKE2b hash of the event body. Content addressing is what makes the log tamper-evident: change one byte of an event and its ID no longer matches, which breaks the chain. Signing is optional Ed25519 per event, so you can prove which actor created what. The materialized view is a sled embedded key-

Dipankar Sarkar 2026-07-25 23:24 7 原文
AI 资讯 Dev.to

The Two-Map Party Game Server: Building GameNight Without a Database

Every party game app I'd used before building this one wanted an account, a lobby website, or a subscription. I wanted the opposite: plug a laptop into the TV, run one command, and have everyone's phone connected in under thirty seconds — no internet required once the LAN is up. That constraint ends up dictating almost every architectural decision in GameNight : a Node/Express/Socket.io server that runs five real-time party games — a Mafia-style social deduction game I call Mongolpuri, UNO, a trivia quiz, Scribble, and Tic-Tac-Toe with tournament brackets — entirely from two in-memory Map s, no database, no auth, no build step on the frontend. Decision 1: A room is a plain object, not a schema const rooms = new Map (); // roomCode -> room const playerRooms = new Map (); // socketId -> roomCode const room = { code , gameType , host : socket . id , players : new Map ([[ socket . id , { id : socket . id , name , avatar }]]), status : ' lobby ' , gameState : null , timers : [], settings : defaultSettings ( gameType ), sessionStats : {}, }; Every game's state — the UNO deck, the Killer/Doctor night phase, the Scribble canvas buffer — lives in room.gameState , an untyped bag shaped differently per gameType . There's no ORM, no room class hierarchy, no GameEngine interface every game implements. Each game gets its own set of top-level functions ( startKD , kdResolveNight , startUno , unoPlayCard , …) that read and mutate room.gameState directly, dispatched through one handleAction switch: function handleAction ( room , socket , data ) { const gs = room . gameState ; if ( ! gs ) return ; switch ( room . gameType ) { case ' tictactoe ' : /* ... */ break ; case ' killerdoctor ' : kdAction ( room , socket , data ); break ; case ' scribble ' : scribbleAction ( room , socket , data ); break ; case ' uno ' : unoAction ( room , socket , data ); break ; case ' quiz ' : quizAction ( room , socket , data ); break ; } } For a five-game server built by one person, this is the right amo

Abhijat Chaturvedi 2026-07-25 23:21 6 原文
AI 资讯 Dev.to

No Backend, No Build Step: A Spaced-Repetition Chrome Extension That Runs on chrome.storage.sync Alone

Most "save this for later" tools I've used eventually want a server: an account system, a database for your notes, a sync service with its own outage history. I wanted something narrower — capture text or a whole page while browsing, turn it into a spaced-repetition flashcard, and have it show up on my other machine — without running any infrastructure at all. MindStack is a Manifest V3 Chrome extension that does exactly that: capture, spaced-repetition scheduling, a full dashboard, and cross-device sync, built entirely on chrome.storage.sync and chrome.identity . No backend, no bundler, no npm install before you can load it unpacked. Here's what that constraint forces you to get right. Decision 1: The scheduler is SM-2-shaped, not SM-2 Spaced repetition apps usually reach for a full SuperMemo SM-2 implementation — ease factors computed from response quality on a 0–5 scale, per-review interval history. MindStack's actual scheduler is a compressed version that captures the two properties that matter for a lightweight capture tool and drops the rest: const scoreReview = async ( score ) => { const memory = state . memories . find (( item ) => item . id === activeReviewId ); const interval = { forgot : 1 , hard : Math . max ( 1 , Math . round (( memory . reviewCount || 1 ) * 1.5 )), good : Math . max ( 2 , Math . round (( memory . reviewCount || 1 ) * ( memory . ease || 2.5 ))), easy : Math . max ( 4 , Math . round (( memory . reviewCount || 1 ) * (( memory . ease || 2.5 ) + 1 ))) }[ score ]; const updated = { ... memory , reviewCount : ( memory . reviewCount || 0 ) + 1 , successCount : ( memory . successCount || 0 ) + ( score === " forgot " ? 0 : 1 ), ease : Math . min ( 3.4 , Math . max ( 1.3 , ( memory . ease || 2.5 ) + ({ forgot : - 0.35 , hard : - 0.12 , good : 0.05 , easy : 0.16 }[ score ]) )), nextReviewAt : addDays ( interval ), }; Two properties, deliberately preserved from SM-2: intervals grow multiplicatively with review count (so a card you keep getting righ

Abhijat Chaturvedi 2026-07-25 23:21 6 原文
AI 资讯 Dev.to

Every EnvCastError Tells You How to Fix It: Designing Error Messages as a Feature

int(os.environ.get("PORT", "8080")) fails constantly in ways that waste your time: ValueError: invalid literal for int() with base 10: 'abc' . No variable name. No hint about what a valid value looks like. You grep the codebase for PORT to even find where the read happened. specenv is a zero-runtime-dependency Python library for typed environment variable loading — casting, validation, schema grouping, prefix namespacing. All of that is useful, but none of it is the actual design decision worth writing about. The decision that shaped everything else was: every error must name the variable and say how to fix it, unconditionally, with no opt-out. Decision 1: The error message is generated at the failure site, not templated afterward It would be easy to build one generic EnvCastError(var_name, raw_value, target_type) and format a message from those three fields in __str__ . specenv doesn't do that — each cast failure builds its own message inline, at the point where the specific failure is known: if cast_type is int : try : return int ( raw ) except ValueError : raise EnvCastError ( f ' Cannot cast { name } = { raw !r} to int. \n ' f ' → Set { name } to a valid integer (e.g. { name } =8080) ' ) from None if cast_type is bool : ... raise EnvCastError ( f ' Cannot cast { name } = { raw !r} to bool. \n ' f ' → Set { name } to one of: 1/0, true/false, yes/no, on/off ' ) The generic version would produce "Cannot cast PORT='abc' to int" and stop there. The inline version gets to add (e.g. PORT=8080) for ints, 1/0, true/false, yes/no, on/off for bools, a namespaced hint for prefixed variables — because at the point of failure, you know exactly what a correct value looks like for that type, and a generic formatter three calls up the stack doesn't. The cost is a few lines of duplication across _caster.py 's type branches. That's a fair trade for every single error message being genuinely actionable instead of generically accurate. Decision 2: Missing-and-required collapses to t

Abhijat Chaturvedi 2026-07-25 23:20 5 原文