I build AI features end to end: the model, the service that serves it, and the interface on top — prototype to production, without a handoff. Much of my work is local-first by default, so retrieval and inference run on your own infrastructure and customer data never leaves it. My fixes are merged into NVIDIA, Hugging Face transformers, openclaw and n8n, mostly the unglamorous kind — a silent data-loss bug, a cross-platform breakage, a validation guard that let NaN through, a missing CI pipeline — each one pinned with a regression test. I'd rather ship what survives production than what demos well.
| 🔴 🟡 🟢 ~/yigit |
|---|
|
- Maestro: Your AI agent team. You write one prompt, the agents split the work, and a reviewer checks what they produced.
- J.A.R.V.I.S: Your OSINT analyst. It searches the open web and writes the full report for you, using local models only.
- OracleX: Your trading desk. A local Llama reads market news and scores signals for crypto and stocks.
- awesome-rag-production: Maintainer. A curated list of RAG tools that hold up in production.
| AI / ML | |
| RAG / Data | |
| Backend | |
| Frontend | |
| DevOps | |
| Security |
openclaw/openclaw
· 21 merged
- P0 release blocker: OpenClaw installed but the managed Gateway never started for Windows users whose profile path holds non-ASCII characters outside the CJK range — the generated
.cmdlauncher was written in UTF-8, butcmd.exeparses batch files with the boot-time OEM code page. Fixed across 15 OEM code pages; verified on a Turkish host (ev-yiğit-öğün, ACP 1254 / OEMCP 857) going fromMODULE_NOT_FOUNDto a clean start, with byte-identical output on CJK hosts (PR #108967) - A compacted session's summary grew a copy of itself every cycle, burning tokens on each following turn: whenever a later split degraded, staged compaction re-added a summary chunk 0 already carried (occurrences 2 → 1 after the fix). Fixed in 14 source lines by keying the fallback on whether the oldest split degraded — the one signal the consumer actually asks for — with regression coverage at both layers (PR #109828)
- Every file an agent created on Windows came back lowercased — any name not already lowercase — so the import it had just written broke for all teammates on Linux and in CI; Turkish names like
İstanbul.mdcame back corrupted, not merely lowercased. The sandbox helper was returning a comparison key as a real path — fixed with a case-preserving normalizer, every boundary verdict proven unchanged (PR #109823) - Every truncated turn was billed by the provider but recorded as free — zero tokens, zero cost, and a
stopreason marking it successful, so nothing downstream could retry or warn and per-session accounting drifted low. Any answer hitting the output cap endsincomplete, and that event matched no terminal branch in the agent transport. Fixed by finalizing both terminal events through one canonical usage mapper shared with the package side (PR #109904) - A reply whose code fence opened with a long info string was dropped outright: the chunker reopened the block on every continuation with that full opening line while budgeting only the closing marker, so chunks ran past Discord's 2000-character limit and were rejected with HTTP 400. On a worst-case fence, 1534 chunks with 41 over the limit became 4 that all send, with the body intact instead of 41 of 1290 characters surviving (PR #110148)
- Invalid credentials took 7093ms and 4 requests to report; now 3ms and 1. The retry loop threw its non-retryable errors inside the
trywhosecatchtreats everything as a retryable network failure, swallowing its own classification. Rethrown as a distinct type, same message to the user (PR #110655) - Reef died permanently to relay rate limiting and had to be restarted by hand — a throttled startup failed the whole account, the supervisor's ten restarts each hit the same relay, and the retry traffic fed the throttling that caused it. Startup now shares the periodic reconcile's failure policy: the account comes up on the peer keys it already has and refreshes on the next interval (PR #110918)
- A rate-limited turn failed instead of simply waiting out the server's cooldown: when a 429 carried an unparseable
retry-after-ms, the validRetry-Aftersitting next to it was never read, so the client fell back to blind exponential backoff and burned its attempts inside the window. The two headers are ordered preferences, not alternatives — returning only on a successful parse restored that. Measured against a real socket, the retry gap went from 1016ms of blind backoff to the 3018ms the server asked for (PR #111353) - Coloured command output came back as literal escape codes —
npm install, a coloured test run, adocker build— whenever a sequence happened to straddle a stream read, and the corrupted text landed in the transcript the model reads. The remote bash path already kept a per-stream ANSI parser; the localexecruntime still called the stateless helper on every chunk. Wired the same parser in, with separate state for stdout and stderr so one stream can't consume the other's pending sequence — proven against a real child process splitting a sequence across a real pipe (PR #111364) - Every OpenRouter agent turn dropped its cache-write tokens and billed them as ordinary input — the agent lane hard-coded
cacheWrite: 0while its sibling parser three files over already read the field and documented the contract. Correcting the mapping alone would have silently shrunk every overflow decision on that lane, because the context-overflow fallback was accidentally exact only whileinputabsorbed the writes — so both land together, proven over a real socket with a real transport:80/0→70/10tokens, and a real overflow that the half-fix reports asfalse(PR #111435) - A browser that never came back permanently bricked tab tracking for its profile — from then on every
browser openopened a tab, closed it again, and errored, with manual state clearing the only recovery. Cleanup defers whenever it cannot prove ownership of a tab and nothing ever dropped a row by age, so rows for a dead browser were re-claimed, failed and deferred every 5 minutes forever, until they filled the tracking store's 5,000-rowreject-newcap. Bounded the retry by retiring only rows whose ownership probe already failed and that have gone unused past a 24h window — unlike a namespace TTL, which would also expire tabs that are alive and reachable. Proven end to end against real Chrome over real CDP and real SQLite, across two processes so the row is read back after a restart, withnowthe only injected input (PR #111307) - Host execution blocked
CC,CPPandCXXas compiler selectors but still acceptedCXXCPP— GNU Autoconf's C++ preprocessor selector, the exact counterpart of theCPPthat was already blocked everywhere, so an operator-chosen preprocessor executable could still reach a host build through the inherited or the requested environment. Closing the C++ half of the same rule makes the boundary explainable instead of accidental: one canonical policy key, mirrored into the generated Swift policy and the reported baseline (265 → 266 entries). Proven with the production sanitizer against a real child process — inherited and requestedCXXCPPboth becomenull, the requested one is reported inrejectedOverrideBlockedKeys, benign controls survive and the child exits 0 with noCXXCPPin its environment (PR #112684) - The
edittool refused a perfectly unambiguous edit as ambiguous — "Found 2 occurrences … the text must be unique" for text that occurs exactly once — whenever another line in the file differed only by trailing whitespace, a smart quote, an en-dash or a non-breaking space, which files mixing straight and curly quotes hit routinely. The match and the safety check ran in two different string spaces: the exact path found the text in raw content, then the uniqueness gate counted after normalization had folded away the very distinctions the match relied on. The cost fell on the model, which is told to add context and so retries with a largeroldTextthat spans another near-duplicate and is refused again. Counting in whichever space the match was actually found in fixes it, with a control case proving a genuinely fuzzy-ambiguous edit is still refused (PR #115738) - The compat layer that makes xAI, Venice, Fireworks and LM Studio models usable reported success while leaving the banned keyword in the request — the provider refused the tool call anyway, so from the user's side the model simply could not call the tool and the setting looked like it did nothing. The strip walked five schema containers and copied everything else through verbatim, so a keyword nested under
additionalProperties,prefixItems,patternProperties,contains,$defsand six more survived — andadditionalPropertiesholding a schema is the ordinary way to describe a dictionary, common in MCP tool definitions. Fixed by mirroring the container sets its own caller already agreed on. Proven over a real socket through the real transport, nofetchstub:additionalPropertiesarrives as{"maxLength":100,"type":"string"}onmainand{"type":"string"}here, with three regression tests that each fail against the unfixed source (PR #115741) - One unparseable streaming frame pasted the model's own output straight into the error surface — tool results, file contents it had just read, any credential it had generated — because the Anthropic provider built its parse-failure message out of the raw frame, embedding both the
datapayload and every raw line. The answer already existed one directory over: the canonical Anthropic transport converts only aSyntaxErrorinto a shared malformed-fragment marker and keeps the original error ascause, carrying no payload. What makes this a broken user-facing contract rather than a merely verbose error is who reads that marker — the shared assistant-error formatter matches it by exact string equality and swaps in "LLM streaming response contained a malformed fragment. Please try again.", so the provider path, emitting a different string, never reached that substitution and the operator saw the fragment itself. Aligning the provider with the marker took the operator-facing text from 402 bytes of echoed payload to a 54-byte retry message. The neighbouring throw is deliberately left alone: its structurederrorbody is parsed downstream into a meaningful operator message, so redacting that one would be the regression. Proven over a real loopback SSE server through the Anthropic SDK and the production provider stream, with a before/after mutation run showing the sentinel leaking on the base behaviour but not on this branch, and a well-formed stream as the control case (PR #116938) - The same broken contract, one provider over: a malformed frame on a ChatGPT-login model surfaced the parser's own internal wording instead of the actionable retry message every other provider gives, because the Codex SSE boundary rethrew
JSON.parse's failure as a string of its own. Three separate consumers match the shared malformed-fragment marker by exact string equality — the assistant error formatter, the sanitize path, and the fallback sitting directly under the embedded agent's "never return raw unhandled errors" comment — so none recognised it and that last one returned the raw text verbatim. Fixed the way the canonical Anthropic transport already does it: catch onlyJSON.parse'sSyntaxError, convert it to the shared marker, keep the original ascause, and yield outside the catch so aSyntaxErrorinjected by a consumer throughiterator.throw()still propagates untouched. The WebSocket twin in the same file is deliberately excluded, with the reason written into the PR — a line-level scan of open PRs found one asserting on that exact message text and another restructuring the block. Operator-facing text went from 79 bytes of parser wording to the 54-byte retry message, proven over a real loopback SSE server through the production stream, with the mutation run as the before column and a well-formed stream as the control (PR #116966) - An oversized Google auth response left its socket open behind the expected size error, and nothing was logged — the size guard in the Google Chat auth transport reads
content-lengthand throws before it ever reachesresponse.body.getReader(), so thefinallyreleased the SSRF-guarded dispatcher with a live, unread body sitting behind the failure. I found this second guard site while reviewing the fix for the first one (PR #111290, someone else's): that one merged covering only the API wrapper, and this is the follow-up it invited — same predicate, same deliberately non-awaited cancellation, so both guard sites in the extension now read alike, and only the guard's own rejection path is touched. Proven with a loopback server, the real guarded fetch and the realrelease, with the wrapper only recording whatrelease()observes before delegating:bodyUsedgoes fromfalsetotrueon the oversized path, while a normally read response is unchanged as the control (PR #115873) - The assistant’s own prior turn was replayed to the model with its sentences fused together on every OpenAI-compatible provider — OpenRouter, Groq, DeepSeek, Together, LM Studio, Ollama — because
convertMessagesflattened a multi-block assistant turn withjoin(""). Two text blocks came back as"Let me check the file.The file contains X.", and since that corrupted text is what the model reads as its own previous turn, the damage compounds with every subsequent request. Two text blocks in one turn is routine rather than a corner case: streaming opens a new block after any tool call, and cross-model replay converts athinkingblock into atextblock adjacent to the real answer. Every neighbouring path already disagreed with the choice — the thinking blocks a few lines below join with `
, and flattenCompletionMessagesToStringContent, the helper performing this exact operation for strict OpenAI-compatible servers, joins with
— so the fix follows the closest sibling instead of inventing a separator. Proven on the wire rather than in a mock: a realnode:httpserver stands in for the provider, parses the actual request body and answers with a real SSE stream through the realstreamOpenAICompletionspath, showingfile.The fileonmainagainstfile.
The file` here (PR #115743)
- Generating speech produced an unplayable file instead of the provider's error on xAI, Gradium, Azure Speech and OpenAI: whenever one of them answered HTTP 200 with a body that was not audio — a JSON error, an
application/problem+jsonpayload, an HTML sign-in or captcha page, or zero bytes — that body was read straight into the buffer and delivered as a voice message, while the provider's actual message was discarded. The repository already owns this contract and one speech path already used it, so whether a malformed 200 was caught depended only on which provider a user happened to route through. The split form (assertProviderBinaryResponseContent+readResponseWithLimit) is applied rather than the combined helper, because the combined one hardcodes its own overflow handler after spreading caller options and would have rewritten each provider's existing byte-cap message — the same reason two video-side extensions use the split form. Cancellation stays deliberately non-awaited: under debug-proxy capture the body is one branch of aResponse.clone()tee, and cancelling such a branch never settles while its sibling is live. Proven against a realnode:httpserver that answers 200 and then never ends the body, driven through the real globalfetchand the real SSRF guard with no stubs: the unguarded path hangs to its deadline with the socket still open (5029 ms) against a named error and an operating-system-observed socket close here (120 ms). The mutation control shows the defect in its rawest form — with the guard removed the malformed cases resolve toBuffer[ 123, 34, 101, 114, 114, … ]andBuffer[ 60, 104, 116, 109, 108, … ], which are{"errand<htmlhanded back as the audio a channel then sends. The maintainer's follow-up added the fourth owner,extensions/openai/tts.ts, and audited all eight bundled TTS owners to confirm the gap was exactly these four (PR #117345) - Kilocode models were registered with a context window larger than the model can actually accept — by up to 3.8x, on 33 of the 335 models the live catalog serves. Kilocode's gateway returns an OpenRouter-shaped catalog in which
context_lengthis the catalog-wide ceiling across every routing candidate, whiletop_provider.context_lengthdescribes the primary provider that actually serves the request; discovery read only the first, so context budgeting and the model metadata users see both overstated what a request could use. The repository already owns the correct precedence —extensions/openrouter/provider-catalog.tsprefers the primary provider and falls back to the catalog-wide value — and the same normalization had just landed insrc/agents/model-scan.ts(PR #110855, someone else's, where I had measured and resolved the merge conflict); the Kilocode reader was the surface that pass missed, so this is a completed rule rather than a new one. What made the claim measurable rather than plausible is that the catalog is public:api.kilo.ai/api/gateway/modelsanswers with no key and no inference call, so the real 346-row response was fed through the actualdiscoverKilocodeModels()implementation with the production file as the only variable between the two runs —nvidia/nemotron-3-super-120b-a12bregisters 1000000 against a primary provider offering 262144,minimax/minimax-m31048576 against 524288, and both come back correct on this branch. Completion tokens are deliberately left untouched: enumerating the key union of every row in that same live response shows no top-levelmax_completion_tokensormax_output_tokensanywhere, so copying the sibling fix's fallback chain would have encoded a field the data never carries — which is why the production delta is 6 lines added and 1 removed against 53 lines of tests. The added coverage is shown load-bearing by reverting the production file tomainwhile keeping the new tests, which fails the precedence case with 1048576 where 524288 is expected (PR #118868) - Generating video through BytePlus or Runway hung until a surrounding timeout instead of reporting the provider's own failure, for anyone running with the debug proxy enabled — the path that rejects a successful HTTP 200 whose body is not video (a JSON error, an HTML sign-in page) never returned, so the
malformed video responseerror the provider already knew how to raise never reached the user. The debug proxy installs a global-fetch patch that clones every captured response, and the repository states the consequence in its own source: cancelling one branch of a tee "never settles (it only resolves once BOTH branches cancel)" — yet both providers awaited exactly that cancel, while the sibling branch was still being read by a capture that is deliberately not awaited. Fixed with the fire-and-forget release the other generated-media owners already use, the same decision merged for the Ollama setup path in #111802; scope held to the two rejected-download call sites, with validation, error wording and byte caps untouched. My first proof built the tee by hand withclone()and the review refused it — correctly, since a modeled tee never exercises the shipped patch — so the second round measured the real thing:initializeDebugProxyCapture()asserted installed, a realnode:httploopback origin that sends headers and then never ends the body, and the real provider entry point downloading over real undici through that patched global fetch. Capture on withawait cancel()returned no result inside a 10-second budget; the other three cells of the 2×2 matrix all failed in 14–64 ms with the intended error, which is what isolates debug capture rather than the loopback origin as the cause. The regression tests are shown load-bearing by reverting only the two production lines: both fail at the deadline rather than on an assertion, and the pre-existing cancellation tests pass either way — their stubbedcancelresolves immediately, so they could never see this (PR #119257)
n8n-io/n8n
· 2 merged · both released
- Every Salesforce Case given a Parent ID still landed with
ParentId: null, and the node reported success — the field is declaredParentIdin the node description, but both the create and update handlers read the lowercaseparentIdoff the collection, so the key was alwaysundefinedand the parent was never put on the request. Nothing surfaced the loss: Salesforce was simply never told. Reading the correctly-cased key restores it with no migration, since saved workflows already store the value underParentId— and the two existing tests that had mirrored the buggy lowercase key were corrected alongside new regression tests pinning create and update (PR #33775, shipped in n8n@2.32.0) - An AI agent whose HTTP tool call failed was told the status code and nothing else — never the server's own explanation, so a 403 carrying
{"error":"insufficient_scope","required":"read:users"}reached the model as a bare "Forbidden" and it retried blind instead of correcting the request. The tool built its response fromhttpCodepluserror.message, which left the branch that returns the body unreachable. Confirmed against a realNodeApiErrorassembled from an axios-shaped 403:causeandresponseboth come backundefinedwhile the body sits untouched oncontext.data— the payload was present the whole time, just never forwarded. The body now reaches the model, bounded on every axis that could turn a failure into a worse one: truncated so a long error can't eat the context window, binary and empty payloads skipped, credential-shaped values masked with the redaction patterns n8n already applies to skill tool output rather than a scheme invented for this path, and a serializer that cannot itself throw while an error is being handled (PR #34509, shipped in n8n@2.34.0)
huggingface/transformers
· 3 merged
- Anyone fine-tuning GIT since v4.49.0 trained it to predict two tokens ahead —
GitForCausalLMshifted its labels by hand and then passed them positionally, soshift_labelsstayedNoneand the loss helper shifted a second time; because the manual shift flattened to 1-D first, the pad-and-slice kept shapes consistent (N → N+1 → N), nothing raised, and each row's final target was silently pulled in from the next row in the batch. GIT can't simply drop the manual shift the way the earlier Moonshine fix did — its logits carry leading image positions that must be sliced regardless — soshift_labelsis passed explicitly, on 2-D tensors, which also removes the cross-row leak. Loss went from4.5848(matching the double shift) to4.6461, exactly the aligned cross-entropy (PR #47395) - Seven multimodal models raised outright under mixed precision —
Trainer(bf16=True), or any Accelerate autocast context — because they moved the encoder output to the text stream's device beforemasked_scatterbut not to its dtype.nn.Embeddingis not on autocast's cast list, soinputs_embedsstays float32 while the encoder's finalnn.Linearreturns bfloat16, andmasked_scatterrequires both operands to share a dtype: under the ordinary mixed-precision setup, not an edge case. Rather than guess the blast radius from model names, I listed all 121masked_scattercall sites undermodels/— 104 already align the dtype, 17 do not — and put every one of the 17 through the same autocast forward, built from the model's ownModelTesterwith no pretrained weights: seven raised, three were already safe because their scattered tensor comes from an embedding table in the same module, and the rest scatter structurally different things. The PR covers exactly the seven that raised, with the excluded sites tabulated in the body and the already-fixed Gemma 4 path kept as a control;pi0is a vision model, so the "audio family" cut I started with would have missed it (PR #47673) - Two more models raised under the same mixed precision setup, and the sweep that fixed those seven could not have reached them —
kosmos2andkosmos2_5move the vision features to the text stream's device before merging them intoinputs_embeds, but not to its dtype, so underTrainer(bf16=True)or any Accelerate autocast context the merge dies withIndex put requires the source and destination dtypes match. Same root cause as #47673, different operator: that PR's scope came from enumerating everymasked_scattercall site, and these two models merge with an advanced index assignment, which lowers toindex_put_— just as unable to type promote, and outside that enumeration by construction. Neither has amodular_*.pyeither, so nothing propagated into them from a sibling. The fix is the oneidefics2,idefics3andmodernvbertalready apply at the identical merge point, and both models were put through a bfloat16 autocast forward built from their ownModelTester, raising before and clean after.smolvlmcarries the same defect, but #41485 is already open against that file, so it is named in the PR body and deliberately left to that author instead of being duplicated (PR #47691)
langchain-ai/langchain
· 1 merged · co-authored
- A one-character typo turned a human-approval gate into unattended execution.
HumanInTheLoopMiddlewarelets you put a risky tool behind a human by listing it ininterrupt_on, butInterruptOnConfigis aTypedDict, so nothing checked those mappings at runtime — and the resolution loop kept an entry only whileallowed_decisionswas truthy.interrupt_on={"delete_database": {"allowed_decision": ["approve"]}}— missing thes— was dropped silently:after_modelreturnedNone, and the tool ran with no interrupt, no error and no warning. The same held for an emptyallowed_decisionsand for a config carrying onlywhenordescription. The user's intent is unmistakable in every one of those spellings, which is what makes this the worst possible failure direction for a safety middleware: the misconfiguration disables the protection it was written to configure, and nothing surfaces it. I reported it (issue #38838) and wrote the fix — move the check to construction, so an entry that would have been silently discarded raises instead. My PR (#38959) was auto-closed 23 seconds after opening by the repository'srequire-issue-linkbot, which closes any PR whose author is not assigned to the linked issue; the report was then picked up by @imnishitha, who landed the same construction-time validation as PR #39247 and merged it —ValueErrornaming the offending tool and echoing back the keys the config actually carried, which is what turns a typo from a side effect discovered in production into a message at startup. Credited as co-author on the merge commit (5909891)
NVIDIA/TensorRT-LLM
· 2 merged
SamplingParams(top_p=float("nan"))passed validation and reached the sampler — the same formin_pandtemperature— because those three range checks were written in the positive form (value < low or value > high), and every comparison against NaN returnsFalse, so the guard whose entire job is rejecting out-of-range values waved through the one value that has no place on the range at all. Nothing downstream re-checks it: the request is accepted, the value flows into the sampling path, and what the user gets back is unusable output instead of theValueErrorthe API already knows how to raise fortop_p=1.1. The fix is the negated form (not 0 <= top_p <= 1,not 0 <= min_p <= 1,not temperature >= 0), which for every non-NaN input is the exact complement of the condition it replaces — the accepted set is unchanged at every boundary, and NaN is the only value that moves. That form was not invented here: thetop_p_decayandtop_p_minchecks a few lines below in the same method already use it, so what reviewers were asked was not "should this behaviour change?" but "why does this line disagree with its neighbours?" — deliberately the shorter argument, and the one that carries a merge. Scope was held to that:temperature=infstays accepted because it flattens the distribution rather than corrupting it, and the small-temperature clamp requested by #15715 is left alone because it changes the behaviour of valid input and belongs in its own PR. Filed first as issue #17158, since this repo enforces issue-before-PR in practice. The regression tests pin all three NaN cases plus out-of-range and in-range boundary controls — and they arrived unable to run:tests/unittest/llmapi/test_sampling_params.pyexisted but was in no test list, so a second commit registered it in the L0 CPU pre-merge list, which also put the file's pre-existing tests into CI for the first time. Merged after a fullL0_MergeRequest_PRrun (PR #17159)- A streamed response that ended mid-tag silently lost the last characters the model produced —
DeepSeekR1Parser.parse_deltawithholds a trailing fragment that could still grow into a<think>/</think>delimiter, parking it inself._bufferuntil the next delta arrives.BaseReasoningParser.finish()exists precisely so a parser can flush that state when the stream ends, and the serving layer does call it (serve/postprocess_handlers.py:177,serve/responses_utils.py:963) — butDeepSeekR1Parsernever overrode it, so a response ending in a literal<, or one cut off bymax_tokenspartway through</thin, dropped those characters out ofcontentorreasoning_contentwith nothing raised and nothing logged. The blast radius is every parser key backed by that class —deepseek-r1,qwen3,qwen3_5,laguna,minimax_m2,minimax_m2_append_think— pluskimi_k2,minimax_m3anddeepseek_v4, whosefinish()delegates to the base parser and was therefore a no-op until now. What makes it defensible in 25 lines is that the correct implementation was already in the file twice:NemotronV3ReasoningParser.finish()andGemma4ReasoningParser.finish()both flush exactly this way, so I wrote the new one inGemma4's shape rather than a tidier variant of my own — the question put to reviewers was not "is this behaviour right?" but "why does this class not honour a contract its siblings already do?", andparse_deltawas left untouched. A buffer holding exactly a complete tag is a delimiter rather than model output, so it is still discarded, which keeps the existing handling of a stray closing tag as the final delta. The tests are where review actually landed: I shipped 37 parametrized cases, and the reviewer asked for the minimum set instead, because CPU pre-merge runtime is a cost every PR in the repo pays — a good rule, and one I had inverted. Cut to 6, keeping a streaming-vs-non-streaming equivalence property test that subsumes the example-based ones, one(parser_key, text)pair per branch offinish(); 4 of the 6 fail againstmain, the other 2 exist to catch a wrong fix that leaks a delimiter or flushes from the identity parser. Writing that equivalence test turned up something wider than the bug I had filed: enumerating every string over["a", "<think>", "</think>"]up to length 3 and comparingparse(text)against streaming it character by character, 20 of 39 texts diverge forqwen3and 13 of 39 fordeepseek-r1— none of it a regression from this PR, so it went out as its own report (issue #17296) rather than expanding this one. Filed first as issue #17156; merged after fiveL0_MergeRequest_PRruns, the first of which failed on two AutoDeploy MoE tests that were a knownmainbreakage waived hours earlier than my branch's base (PR #17157)
koala73/worldmonitor
· 6 merged · 1 prototype
- A CI job went red on an assertion about a Redis transport, in a file the change under review did not touch — 22,600 tests passed and one lost a race by about a millisecond — the endpoint rate limiter arms two deadlines whose order is load-bearing:
ENDPOINT_RATE_LIMIT_TIMEOUT_MSbounds the SDK's availability-first decision race, andENDPOINT_REDIS_ABORT_TIMEOUT_MSarms anAbortSignal.timeoutthat cancels the underlying Upstash fetch just before it. The abort has to land first, or the decision returns while the request is still pending in the isolate — which is exactly what the failing test asserts. The reason it is a race at all is that the two timers do not start at the same moment:timeoutstarts whenlimit()is called, but the abort signal comes from a per-request factory the Upstash client only invokes when it builds the request, so the real comparison isarmingCost + abortMsagainstdecisionMsand the configured gap has to cover that arming cost. In production it does, comfortably — the gap is 500 ms and arming takes a few milliseconds. The test-context pair mirrored the production ratio rather than its margin, at 25/20, leaving 5 ms against an arming cost that under the node runner is 8–71 ms, because every case re-imports the module throughtsxwith a cache-busting query. Rather than call it flaky and ask for a re-run, I instrumented the exact path with the test's own stub fetch: the abort was winning by 1.1–16.3 ms across ten runs on an idle machine, which is not a margin but a coin flip, and--test-concurrency=16on a two-core runner is what flips it. Widening the test-context decision deadline to 250 ms takes the headroom to 164–220 ms; the production values are byte-identical and the abort stays at 20 ms, so failure paths still resolve fast and the one case that genuinely waits out the deadline costs 0.12 s more. I rejected the obvious alternative of polling for the abort with a bounded deadline, because it quietly changes what the test claims — the name is abort … before failing closed, and an eventual-cancellation assertion would pass even if the ordering regressed for real — and rejected scaling the production ratio down (250/225), because a 25 ms gap is still inside the observed arming range. The new gap is pinned by a mutation-proven guard: revert 250 to 25 and it fails withgap is 5ms(PR #6405) - The GDELT intel seeder had two readers of the same stored timestamp, and only the one that did not need a forward-skew guard had it —
rankTopicsForFetchclamped every storedfetchedAtto the run clock before ordering the fetch queue, whilecontentMeta— the reader whosenewestItemAtis whatmaxContentAgeMin: 1440is actually evaluated against — took a bareDate.parseand accepted any finite positive result. The path carrying the guard was the path that could not alarm; the path that feeds the alarm had none. I corrected the issue's own premise in the PR body rather than shipping quietly against it: it claims a future-dated stamp keeps the cohort falsely fresh indefinitely, and that is not true —api/health.jsalready folds a negative content age intocontentStale, so a wildly future stamp surfaces asSTALE_CONTENTtoday. What the missing clamp actually costs is narrower and still worth closing: the length of the skew window, during which the stamp is ahead of the run clock but not yet negative and the cohort reads fresh; anewestItemAton the wire that an operator cannot reconcile with the ordering view of the very same stamp; and a pre-epoch ISO string, which parses negative, passesNumber.isFiniteand would have been taken as theoldestItemAt, sincecontentMeta'sms > 0filter caught exactly0. The fix is one extractedparseStampMsthat both paths call, with the clamp conditional so an injected test clock andDate.now()behave identically — six mutants, no survivors, and the starvation fixture is the real incident from the linked issue rather than invented numbers. Defect 1 of the same issue —newestItemAtis aMath.max, so one refreshed topic holds the alarm green while five starve — I deliberately did not implement, because the issue names two shapes for it and the one it prefers changes a content-age contract mirrored across three files behind a parity verifier; I costed all three options with file:line and offered to build whichever he picked. He asked for Allow edits by maintainers, so I enabled it and answered with the branch state rather than a rebase he had not asked for. His follow-up commit found the failure mode I had missed: clamping a poisoned persisted stamp still mints fresh health evidence through the cache merge, so the health reader now rejects anything beyond a one-hour skew tolerance —contentMetareturns null,newestItemAtpublishes as null, and the classifier reads that asSTALE_CONTENT, which is the fail-closed direction — while the ordering reader keeps the clamp, because it needs a usable sentinel rather than a rejection. That commit also changedrunSeedto hand every seeder's fetcher an immutable run clock, so I audited the blast radius instead of assuming it: all 109 namedrunSeedfetchers, 92 of which take no parameter and 10 of which destructure an options object with defaults, none declaring the new key. The single red check was an unrelated flake in a rate-limiter test, and rather than call it flaky I measured it — the abort deadline was winning its race by as little as 1.1 ms, because the abort signal is armed lazily while the decision timer starts immediately — and sent the margin fix out separately as #6405 (PR #6044) - The analytics collector's outage alarm sat at its ceiling through the busiest, entirely healthy hour of the day — 67,394 events, zero downtime, 60 of 60 windows breached. The check compared a raw quotient against a fixed 0.5 on a denominator that could be as small as 5:
failures / writes >= 0.5reads the same for 5/5 as for 5000/5000, and those are not the same claim — at the old floor the 95% interval half-width at p = 0.5 is ±0.368, which is to say the rate was not resolved at all. But no sample-size correction alone explains the maintainer's evidence, because that window's rate really was above 0.5; it was simply normal, since ad-blockers make a stable share of collector writes fail for reasons that are not an outage. So the fix is three parts and invents no constant: a Wilson lower bound in place of the point estimate, so sample size becomes part of the claim rather than a footnote; aMIN_WRITESfloor derived as the smallest n whose half-width falls under ±0.15 (n = 31 at 0.1477, n = 30 at 0.1502) rather than picked; and the window judged against that cohort's own prior baseline instead of a universal number, so the alarm fires on a departure from normal.MIN_FAILURE_RATEwas deliberately left at 0.5 and handed back with the reasoning, because choosing it would have meant inventing a calibration from production data I do not have. A side finding:api/analytics-health.test.mjswas in no test script —test:dataglobstests/plus a fixed list,test:sidecarnames api files by hand — so the file existed, carried assertions and had never gated a single merge; wiring it in took the sidecar suite from 306 to 329. What followed is the part worth recording: the maintainer ran mutants against my suite himself and returned the deepest review this account has had, with one real blocker — at p = 1 the Wilson upper bound is exactly 1, not merely clamped, while the observed lower bound at p = 1 is1/(1 + z²/n) < 1always, so a saturated baseline makes the comparison permanently unsatisfiable; and since the counters are client-supplied, that state is reachable on purpose. He had the verified fix and could not push it, because Allow edits by maintainers was unchecked — so I enabled it on the open PR (gh api -X PATCH … -F maintainer_can_modify=trueworks fine post-creation, contrary to the usual folklore), rebased so his patch would land clean, verified his math independently rather than reimplementing it, and spent my remaining effort on the one question he said he could not settle without operator data. He pushed the hardening onto my branch — per-hour baselines instead of a daily mean, a veto ceiling so a saturated baseline loses its veto, bounded browser reports — and merged (PR #6041) - A single newline anywhere in an RSS item forged an extra row inside the analyst prompts — and in the brief seeder's digest block that forged row carried a story hash the feed itself chose —
sanitizeForPromptpreserves a lone\nby design: it splits on newlines to drop role-prefixed lines, rejoins, then collapses only runs of 2 or more whitespace characters. That is correct for a prose body and wrong at every site where the newline is the delimiter of the block being composed, and four such sites were still unguarded. Prediction-market titles are composed into- "${title}" — Yes N% (V volume)rows under a header the deduction prompt presents to the model as crowd-calibrated evidence, so a title carrying a newline mints a market with an attacker-chosen probability and volume; theHeadline:/Description:/Source:rows of the why-matters prompt take the same shape, fixed insidesanitizeStoryFieldsrather than at the call site because the legacy relay path composes an identical row set from that same function — one fix, both paths;_country-brief-context.tsinterpolateditem.titleraw, so it was missing the content sanitization as well, and its sibling block two functions above is safe only accidentally, becauseJSON.stringifyhappens to escape the newline. The Railway seeder'sbuildDigestPromptwas the sharpest of the four: its system prompt instructs the model to key its output off the[h:<hash>]token of each row, so a forged row there does not merely add noise — it introduces a numbered story whose hash the attacker picked, into a brief the product treats as generated from real ones. The upstreambrief-compose.mjsdoes sanitize those fields, but with sanitizers that keep a lone newline: the delimiter guard has to live where the delimiter is. The tests assert the row count the payload itself declares, not the absence of the forged string — a guard that only greps for the payload passes for the wrong reason the moment that string is echoed anywhere — and every guard was reverted one at a time, nine mutants, no survivors. The one residual I could not close without changing the shape of a prompt I named in the body instead of quietly breaking the test that pins it:Context:is the block's single free-prose sink, whose internal newlines are legitimate grounding text, so a body newline can still forge a trailingKey: valuerow. The maintainer merged with a follow-up commit resolving exactly that — splitting the description path so prose keeps its newlines while the labelled metadata rows stay line-safe — and line-sanitizing the digest's[SEV]token, the one field my sweep had missed (PR #5897) - Most of the dashboard's cross-source intelligence signals could not fire at all, and the seeder reported success on every run — the Railway seeder behind
intelligence:cross-source-signals:v1bare-JSON.parsed each of its Redis inputs, so every contract-mode key reached its extractor as the{ _seed, data }envelope it is stored in: the payload array wasundefined, theArray.isArrayguard on the next line was false, and the extractor returned[]without throwing — so the aggregator'stry/catchhad nothing to log and the run still exited 0, publishing a shorter list. Auditing all 21 extractors against the writer of each key exposed a second, independent layer: most also read field names and enum spellings their writer has never published — the wildfire signal filtered onradiativePower > 5000 || severity === 'extreme'against detections that carryfrpin MW and noseverityfield at all, leavingbrightness > 400as its only ever-correct clause, which is why the code reads plausibly and produces nothing. Neither existing suite could see any of this: both reconstructed the module withreadFileSync+ regex +vm, and one of those regexes deleted the reader outright. All 23 corrections are mutation-proven — reverting any single one to the code it replaced turns the suite red, no survivors (PR #5896) - A model ID the provider does not serve cost a wasted round-trip on every single call, indefinitely — the health gate probed
new URL(apiUrl).originwith a bare GET and read any HTTP response as healthy, so it never sawcreds.model: the request was built, rejected withhttp_4xx, and fell through to the next provider, with nothing in the logs pointing at the model as the cause. The evidence was already being collected and discarded — both provider loops read the error body for diagnostics and log the model beside it. Feeding that back into the gate quarantines theorigin|modelpair for 10 minutes after two consecutive rejections whose body explicitly names the model, at no new network cost. Detection is deliberately narrow: 401/403/429/5xx are credentials, rate limits and outages — provider-wide and silent about the model ID — so they never quarantine, and an unreadable body keeps the previous behaviour, making the fail-safe the status quo rather than a wrongly quarantined model. Pinned by a behavioural test that sends four calls at a dead model: 4 attempts againstmain, 2 here (PR #5458) - Designed and prototyped the client-side RAG pipeline that gave AI intelligence briefs historical context — embeddings and cosine similarity running in a Web Worker over an IndexedDB vector store, so retrieval needs no server-side index. My prototype (PR #647) was reworked by the maintainer and shipped as PR #675
D4Vinci/Scrapling
· 1 merged · released
- Both bulk browser tools of the MCP server sized their page pool wrongly, in opposite directions — so an AI agent reaching for either one got a batch that could not run at all or one that ran through a single tab.
bulk_fetchpassedmax_pages=len(urls)straight intoAsyncDynamicSessionon the session-less path, butPagesCountisAnnotated[int, Meta(ge=1, le=50)], so any batch over 50 URLs — or an empty list, which trips thege=1bound — died onInvalid argument type: Expected int <= 50 at $.max_pagesbefore a single fetch started.bulk_stealthy_fetchnever setmax_pagesat all, so the pool fell back to the default of 1 and every URL queued behind one tab, raisingTimeoutErroronce the 60s pool wait ran out; the two sibling tools therefore behaved differently for the identical batch. Both call sites now go through one helper that clamps the pool to the validator's own range, so a batch gets a page per URL up to 50 and anything larger is processed through 50 concurrent pages instead of raising — and the cap is stated in the:param urls:line of both docstrings, since those docstrings are exactly what the model receives as the MCP tool description. The tests reuse the file's existing fake-session monkeypatch so nothing launches a browser: both bulk paths at 3/4, 60 and 0 URLs, plus one case that feeds the computed size into a realAsyncDynamicSessionas a regression guard against the validator bounds drifting away from the constant that mirrors them (PR #393, carried into the v0.4.13 release by PR #406 and credited by name in its Bug Fixes notes)
agentscope-ai/QwenPaw
· 4 merged
- The headless
qwenpaw taskcommand could not run a single task, and reported its own failure as the task's —_run_taskpassed a barestrasMsg(content=...), but the pinnedagentscope==2.0.4.post1declaresMsg.contentaslist[ContentBlock]with nomode="before"validator, so pydantic raisedValidationErroron every invocation, before the agent was ever built. The call sits inside a broadexcept Exceptionthat turns any throw into{"status": "error", "error": ...}, which is exactly why this survived unnoticed: the symptom reads as a task that failed, not as a CLI that cannot construct its own input, and the error string it returns is a pydantic validation message about a type the user never chose. The repository had already settled the correct shape — every otherMsg(...)undersrc/qwenpawwraps its content in a block list, and the same function builds the right thing 25 lines earlier forAgentRequest— so the fix adopts agentscope's ownUserMsgfactory rather than hand-assembling aTextBlock, and the PR argues "make this agree with the rest of the repo" instead of asking for trust. Nothing caught it because all 15 tests intest_cli_task.pymonkeypatch_run_taskwholesale, leaving the function at zero coverage; the two sibling occurrences inproactive_responder.pyare named in the PR body as an offered follow-up rather than bundled in, since this repository rejects one fix spread across files. Merged unchanged, 2 source lines against 51 lines of new coverage (PR #6616) - Stopping a local model server on Windows could hang shutdown indefinitely, flash a console window on every poll, and crash outright on a non-UTF-8 console —
_is_pid_running()shells out totaskliston each iteration of the shutdown wait loop'ssleep(0.1), and it was the one call site in its own module that skipped thetimeout, thewindows_hidden_subprocess_kwargs()the module already defines, anderrors=on a locale-decoded read, so a cp936/GBK console raisedUnicodeDecodeErrorstraight out of the shutdown path. Measured on Windows 11 each probe costs ~0.157s, so the intended 0.1s poll actually ran at ~0.257s and a 5s graceful shutdown spawned up to 19tasklistprocesses. Two rounds of maintainer review moved the PR past that surface fix into the two real defects underneath: a failed probe returnedFalse, which callers read as a confirmed exit — so a timed-out probe madeshutdown_process_sync()report a graceful exit and skipkill()for a process still alive — and_PID_PROBE_TIMEOUTwas independent of the caller's deadline while the probe ran before the remaining budget was checked, so a 6s shutdown budget measured 20s in the worst case. Probe failures now assume the process is alive, the wait loop bounds each probe by what is left of its deadline, and the post-deadline path does the free localis_alive()check instead of spawning anothertasklist, letting the caller escalate. The elapsed-budget regression test runs under a virtual clock, so it asserts the 6.0s bound exactly without sleeping in CI (PR #6203) - A shutdown during boot could wipe every recorded day of token usage, silently — cancel the consumer while it is still reading the file (Ctrl-C,
uvicorn --reload, a quick restart) andstop()force-flushes a cache that was never seeded, committing{}overtoken_usage.jsonthrough an atomicos.replace()with no backup and nothing logged. The window only opens for users who have history to lose. Pinned by a regression test and a positive control (PR #6220) - Cut one of three
nvidia-smispawns at startup and half of those per/modelsrequest — 40% off the measured probe time: a CUDA guard re-ran a query that already returns cleanly without a driver (PR #6204)
MadsLorentzen/ai-job-search
· 2 merged
- Hex-encoded accents leaked into the LinkedIn scraper's CLI output as raw entities, and emoji came out mangled in every form — the decoder handled decimal entities only, and
String.fromCharCodetruncated supplementary-plane code points to 16 bits. 1 of 6 fixture cases passed before, 6 of 6 after, under network-free unit tests (PR #55) - Same bug in both duplicated decoders of the Jobindex scraper, where it matters more: on a Danish portal
æ/ø/åfrequently arrive as numeric entities, and their hex forms rendered broken (PR #56)
OthmanAdi/planning-with-files
· 3 merged
- Gave the project its first automated test run: CI until then only reviewed skill prose, never behavior — now pytest across Ubuntu and Windows plus vitest for the Pi extension, on every PR and push to master (PR #199)
- Running that suite on hosted runners exposed two latent cross-platform test failures — a Git Bash path-alias mismatch on Windows and Windows-shaped sanitizer vectors executing on POSIX. Fixed test-side, no production changes, and landed first so the CI PR could go green (PR #198)
- Made those runs reproducible: committed a lockfile for the Pi extension and switched the vitest job to
npm ci(PR #200)
MODSetter/SurfSense
· 7 merged
- Every video presentation was narrated in American English, whatever language its slides were in — a deck built from Chinese source content produced Chinese slides and Chinese speaker transcripts, then handed those transcripts to a pipeline constructed with a literal
lang_code="a"and a hand-rolled voice map returningaf_heart/alloy/en-US-Studio-Oregardless of input. The repository had already solved this once: the podcast package carries a BCP-47 normalizer, a voice catalog with per-language rosters, and aTextToSpeechport whose Kokoro adapter maps the tag to the right pipeline and caches one per language — the video path simply sat outside all of it, which is what let the PR argue "the correct policy is already written in this repo and this one path is outside it" rather than propose a design. The design itself was not mine either: the maintainer's own comment on the issue — unimplemented for two and a half months — specified it (the LLM reports the language since it writes the transcripts anyway, environment variable as fallback, be opinionated about voice defaults), so the work dropped from find a good solution to implement the decided one. Moving up a layer made the diff smaller rather than larger: the node stopped branching betweenget_kokoro_tts_serviceandlitellm.aspeech, itswav/mp3conditional becametts.container, andapp/services/kokoro_tts_service.pywas left without a caller anywhere and deleted along with the module-scopekokoro+torchimport it dragged into the agent. The one thing a language-aware resolver silently breaks is the decks that already work — the catalog listsam_adamahead ofaf_heart, so every existing English presentation would have been re-cast with no error anywhere — so each provider's previous voice is seeded aspreferredand all four literals are pinned by a test named after that regression. 42 tests added and shown load-bearing by reverting only the source files:test_narration_language.pyis not even collectable ondev,test_slide_schema.pyfails 7,test_slide_audio_narration.pyerrors 3 (PR #1660) - A malformed LLM reply printed the user's own indexed documents to stdout — the video agent carried a hand-copied duplicate of the tolerant JSON parser the podcast package already exported, and that copy's failure path ended in
print(f"Raw response: {content}"), so a reply that failed to parse dumped the model's answer — assembled from whatever documents the user had indexed — into the worker log. The helper it duplicated documents itself as the thing that "keeps every generation node validating replies the same way", yet had no caller outside podcasts;git mvtoapp/utils/structured_output.pymade that docstring true and history reviewable. Framing mattered more than the diff:printis on this repo's ruff ignore list, so the PR states in its own body that this is not a style change but a log-level and payload-exposure one, and the neighbouring bareexcept Exception: print(...)becamelogger.warning(exc_info=True)because that branch was swallowing LLM transport failures indistinguishably from parse failures. My first regression test was worthless and I said so in the body rather than quietly replacing it — written againstinvoke_json, it stayed green with the fix reverted, because the leak never lived in the helper; rewritten throughcreate_presentation_slides, both tests fail ondevsources (PR #1661) - The document retriever computed query embeddings on the event loop where its own sibling module offloads the identical call — four call sites across the codebase wrap
embedding_model.embed()inasyncio.to_thread, and two indocuments_hybrid_search.pydid not, so a local sentence-transformer encode would block the loop for every concurrent request. What kept this from being a performance claim is that I checked the callers before writing one:vector_searchandfull_text_searchhave no callers in the repository, andhybrid_search's single caller always supplies the embedding, so the two corrected lines are unreachable today. That measurement went at the top of the PR body under "please read this part before weighing the PR", together with the alternative it implies — an offer to send the version that deletes both unused methods instead, if the maintainer would rather have that. Consistency, not speed, is what the PR asks for, and it is the honest claim (PR #1662) - One unsorted import in a file nobody was touching failed the
Frontend Qualityjob — and with it theQuality Gateaggregator — on every pull request opened againstdev, including ones that change no TypeScript at all. I found it the way it finds everyone: a documentation-only PR of mine (#1665) came back red on a TypeScript check. Thebiome-check-webpre-commit hook declaresalways_run: true, which overrides its ownfiles: ^surfsense_web/filter, together withpass_filenames: falseand a trailing.in the entry — so the--from-ref/--to-refnarrowing the workflow performs never reaches that hook and every PR is measured against the whole web tree. The evidence that the breakage was not mine came from someone else's change rather than my diff: merged PR #1663 carries the same two red checks, anddev's own Code Quality runs had been failing since late July, with the CI log naming exactly one diagnostic across the tree —lib/error-toast.ts,Checked 1055 files. Found 1 error.The fix is Biome's own suggested safe fix applied verbatim, two import lines swapped. What I deliberately did not submit is a number: running the same Biome command locally reported 1056 errors, and that figure is an artifact ofcore.autocrlf=true— my Windows working tree is CRLF, so Biome flagsformaton every file, while the committed blob and CI's checkout are LF. A measurement whose difference from CI you cannot explain is not evidence, so it stayed out of the PR body. I also left the hook itself alone and said so: whetheralways_runencodes a deliberate "always check the whole app" policy is a maintainer's call, and either way this file had to be sorted first. Re-measured after the merge on an LF checkout ofdev, this file is clean — and 12 new diagnostics have since arrived from a feature merge, which is the hook's behaviour restating itself rather than a regression from this change (PR #1666) - The first instruction in the contributing guide was a 404, and the two formatters it named are not the ones the project runs —
CONTRIBUTING.mdsent new contributors to./PRE_COMMIT.md, a file with no match ingit ls-filesand no other reference anywhere in the repository, then told them to format with Black and Prettier while the repo is configured forruff/ruff-formatandbiome;git grep -inE '\bblack\b|prettier'across every tracked.toml,.json,.yamland.ymlreturns nothing, so these were absent tools rather than a second toolchain coexisting, and the practical cost is a contributor runningblack .and producing a diffruff-formatthen disagrees with. The dead link became the install command CI itself uses (.github/workflows/code-quality.yml:35) plus a link to.pre-commit-config.yaml. Writing a replacementPRE_COMMIT.mdwould have restored the exact failure mode being repaired — a second prose copy of the hook list, free to drift from the config — so the guide now points at the config file it describes, and whether to maintain a written guide stays a maintainer's decision instead of being smuggled into a link repair (PR #1665) - One feature merge put twelve diagnostics into the web tree, and from that moment every pull request opened against
devfailedFrontend Quality— whatever it changed. Samebiome-check-webhook as #1666, and I had written in that PR's body that the tree would be clean once it landed. Re-measuring after the merge is what produced this one:lib/error-toast.tswas gone, so the fix held, but the count had gone fromChecked 1055 files. Found 1 error.toChecked 1066 files. Found 12 errors.— the claim had not been wrong, it had expired, because I had cleaned the breakage and not the producer, andalways_run: truewas still there measuring every PR against the whole tree. Nine of the twelve arebiome check --writeoutput, six files formatted at a narrower width than the configuredlineWidth: 100and three with unsorted imports. The other three are a11y rules with no automatic fix, sitting in code the maintainer had merged the day before — and stopping at the mechanical nine would have left the gate exactly as red as before, which is the only thing this PR exists to change, so all twelve went in: anaria-labelcarried by a role-less<div>thatuseAriaPropsSupportedByRolerejects, and tworole="status"containers, each resolved to the native<output>element rather than to a rule-silencing attribute.<output>has the implicitstatusrole and acceptsaria-label/aria-busy/aria-live, so what a screen reader announces is unchanged, withblockadded on the two that replaced block-level elements since<output>is inline by default. Verified with the exact command the hook runs —Checked 1066 files in 797ms. No fixes applied.— and the tree's 38 pre-existingtscerrors counted before and after, none of them in the eight files touched (PR #1671) - The same gate misreading "changed files" the same way in two more tools — and this time it proved itself on my own pull request. I found the Python half while reviewing someone else's contribution (#1648): that PR appends a helper function near the bottom of
app/routes/documents_routes.py, and itsBackend Qualitywas red on an unsorted import block the file already had, fifty lines from anything its author wrote. Nineteen such blocks were ondev, plus an unsorted__all__, anisinstance(x, (list, tuple)), and eleven more files carryingruff formatdrift. Twenty of the twenty-one violations areruff check --fixoutput; the single hand edit is theUP038, because ruff offers that one only as an unsafe fix while the pinned hook runs a plain--fix, so it would have stayed red otherwise. What turned the mechanism from an argument I had been restating since #1665 into a measurement is this PR's own red check: it touches 32 files, every one of them Python, not a line of TypeScript — andFrontend Qualityfailed on it anyway, with exactly the twelve diagnostics #1671 fixes. A backend-only change failing the web gate on files it never opened is the claim stated by the CI rather than by me. A third tool then repeated it:detect-secretsflagged a localhost DSN default that had been ondevsince July 2025, in a file my commit had touched only to add a blank line — because the root.secrets.baselineparses fine and contains zero records, so a gate that looks configured has no allowlist at all and bills the first PR that comes near it. That one is fixed here with an inline# pragma: allowlist secreton the one line that fires, and the empty baseline reported rather than regenerated, since which of the two to adopt repo-wide is a maintainer's call. The review that started all of this also produced the correction I owed its author: I had told themruff checkwas clean on their files, measured on their branch, when CI checks outrefs/pull/N/merge— so the retraction, the mechanism and two ways out went into the next comment, and both cleanup PRs merged 33 seconds apart the following morning, this one with its inheritedFrontend Qualityfailure still showing (PR #1672)
lemonade-sdk/lemonade
· 4 merged
- A streaming chat completion that failed at the backend reached the client as an empty stream that simply ended — no error, no status, nothing to retry on — while the server had in fact sent a precise explanation of what went wrong.
forward_sse_stream()passednullptrwherepost_stream()takes itson_statushook, so the backend's error body was written straight into a response already committed as200 OKwithtext/event-stream. Arriving without adata:prefix, that body is not an SSE event, and every spec-compliant parser drops it silently: the diagnosis was on the wire the whole time, addressed to nobody. What makes this a completed rule rather than a new one is that the repository had already written the answer down twice —post_stream's own header documents the hook as existing so that "callers can divert an error body instead of forwarding it as payload bytes", andforward_byte_stream(), sixty lines down the same file, does exactly that: diverts the non-200 body into a capped buffer and reshapes it into{"error": {message, type, status}}. So the fix adopts that path's shape verbatim rather than inventing a payload, and the two streaming paths now report failures identically. The one open question was the reporter's, and it was a design call rather than a defect: no[DONE]follows the error event, because OpenAI does not send one after an in-stream error andsink.done()already terminates the response cleanly — stated in the body with the one-line reversal, and the issue's author confirmed it was the behaviour they wanted. Proven against a real backend onllamacpp:vulkan, where a context overflow answersrequest (6009 tokens) exceeds the available context size (4096 tokens), try increasing it: the new test asserts every non-empty line is a recognised SSE field and that adata:event carries anerrorobject —test_004already produced this exact backend 400 but only ever asked whether the stream terminated. Review found the assertion too weak and was right: it would still have passed on thebackend_errorfallback the code synthesizes when the body is not already an error object, so it was tightened on two axes — a version-independenttype != "backend_error", which no wording change in llama.cpp can satisfy, plus thecontextsubstring the reviewer asked for. The suite runs intest-cli-endpoints-linuxon every pull request, so the case is guarded on Linux and not just where I measured it. The diagnosis was@ekenberg's: they had opened the issue twelve days earlier offering to send the fix themselves and received no reply, so the body credits the analysis to them and the issue carries an offer to close mine in favour of theirs (PR #2975, issue #2826) - Two C++ test files sat committed in the tree that no CI workflow ever built, and one of them had stopped compiling six weeks earlier without anyone noticing — which is the repository's own argument for the rule it was breaking:
testing.md's "What Reviewers Reject" table lists "committing a test no CI workflow runs", andCMakeLists.txtand.github/between them held zero references to either file.test_ggml_hip_path.cppis the proof the rule earns its place: #2044 added it on 31 May againstis_ggml_hip_plugin_available()inlemon::utils(path_utils.h), #2320 moved that function intolemon::backends::llamacppon 29 June and dropped the header declaration, and the test kept its old include and namespace — 128 lines that read as coverage and compile nowhere. The other,test_model_type_classifier.cpp, is 71 header-only cases overget_model_type_from_labels(), a function called from half a dozen places; it passes 17/17, so it was wired up through theadd_cpp_ci_test(... CI ON)helper the project requires — directadd_test()is overridden to fail precisely so every test makes an explicit CI decision — takingctest -L cpp-cifrom 22/22 to 23/23. The rotted file was deleted rather than repaired, because repairing it meant re-exporting a symbol into a header purely so a test could reach it, and the sametesting.mdrejects "new API surface added only for testability"; the body drew that line and offered the export instead if the maintainers preferred to keep the coverage, and the deletion is what they took. The change also correctstesting.md's three remaining references toregister_cpp_ci_test(), the helperAGENTS.mdnow forbids calling directly. An hour after it was opened, #2950 landed and rewrote the exact table row the docs fix touched, so it was rebased onto their wording with only the helper rename reapplied and re-measured at 23/23 (PR #2976) - Four handlers answered
200 OKwith the router's error object in the body —/rerank,/slots,/slots/{id}and/tokenizeeach passed whateverrouter_->...()returned straight intores.set_content(), so reranking with a model that cannot rerank, or erasing a slot on a backend started without--slot-save-path, reads as success and leaves the client nothing at the transport layer to branch on: every status check passes and the failure is only visible to code that already parses the body looking for it. The rule had been written down the day before I found it.410b8732, a maintainer commit sitting atHEAD~1, introducedset_error_response()— which reads the payload's owntypeandstatus_codeand maps them onto a real HTTP status — and wired the five-lineif (response.contains("error"))guard into exactly one handler,handle_embeddings. The four above are copies of the same shape that predate the helper, so this is not a new convention but the missing half of theirs: +44/-0 across two files, the success path untouched. Only one of the four is testable, and the body said so rather than papering over it — llama.cpp implements/slotsand/tokenize, so on the one backend CI runs their router-error paths are unreachable, and the reranking case (test_018d) is what shipped, with the other two offered if the maintainers wanted them shimmed. Review went past the diff instead:@fl0rianrpushedcef16cf1onto the branch directly, replacing anelif "error" in erase_databranch intest_023_slotsthat had been swallowing exactly this class of failure with an explicit501assertion — the same bug one layer up, in the test written to catch it. That commit carried a 92-character line the repository's pinnedblack==26.1.0resplits; nothing under.github/workflowsactually runs Black despiteAGENTS.mdcalling it "enforced in CI", so CI stayed green and only running the pinned version by hand surfaced it. Reporting that produced the instruction to fix it here, and1ba3cf5eis the resulting one-hunk reformat, verified againstblack==26.1.0in a clean virtualenv (PR #2974) - A request that failed at the backend came back through the Anthropic bridge as
200 OKcarrying an empty text block — and, streaming, as a well-formedmessage_start … message_stopsequence with no error in it anywhere, so a Claude-SDK client saw the model answer with nothing and had no status to branch on. The same overflow prompt measured three ways onllamacpp:vulkanshows how far that one path had drifted from its neighbours: the OpenAI route answers400with the backend's own explanation, the Ollama route500, andPOST /v1/messages200. The streaming half is a sibling my own #2975 created — since that merged, a failed OpenAI stream is framed asdata: {"error": ...}, and the Anthropic adapter, readingid,usageandchoices[0], walked straight past it and kept translating. Both halves land together, because fixing one would have left the endpoint silent only half the time. Review is where the change actually grew, and the reviewer's second objection turned out worse than described:StreamingProxypublishes the upstream status understatuson errors it synthesizes and publishes nothing at all when the backend already returned its own structured error, while the bridge read onlystatus_codeand a numericcode. So the400my two integration tests asserted was a coincidence — llama.cpp happens to putcode: 400in its body, and any backend that omits it would have been reported as a generic500 api_error. The fix moves the producers onto the one field name the repository already reads, injectingstatus_codeinto a backend's own error object rather than adding a third reader, and converts the two remaining emitters ofstatus:trellis_server.cpp, andserve_media_or_error(), which had inlined its own copy of the extraction loop and now callsget_error_status_code()like everything else. The integration tests the review asked for could not be written, and saying so was the answer rather than substituting something weaker: no in-repo backend can drive the mapping past 400 — an unknown model 404s in theauto_load_modelcatch before the bridge is reached, anything that throws lands in the outer catch as a hardcoded 500, and 429/529 have no producer at all without cloud credentials CI does not hold. So the two pure helpers moved tosrc/cpp/include/lemon/anthropic_error.hbehind acpp-ciunit test (ctest -L cpp-ci25/25 → 26/26), which also closed the open question the PR body had handed back —backend_error_http_status()being a private duplicate ofserver.cpp's file-localget_error_status_code(). A second round then named three statuses Anthropic documents that the map still missed — 402billing_error, 409conflict_error, 504timeout_error— and caught that round one's own test had asserted 504 →api_error: that was my incorrect assertion, so the expectation was corrected rather than defended, ending at 28 cases with each of the three red before the change (PR #3006)
openclaw/clawhub
· 5 merged
- Searching the catalog in Japanese never reached the category, topic or summary match tiers — a query whose katakana carries the prolonged sound mark
ー, which covers most of the loanword vocabulary a skill registry is full of (データベース,サーバー,ユーザーインターフェース), matched on name and slug only.tokenize()delegates segmentation toIntl.Segmenter, which handles Japanese correctly, but the text is pre-split on a hand-written character class first, and that class omittedー(U+30FC) and々(U+3005) — so the segmenter received a word already in pieces:データベースarrived as["デ","タベ","ス"], and人々lost its iteration mark outright. Two consequences followed. Exploratory search requires every query token to clear a three-character floor, so rank tiers 2 and 3 were unreachable for these queries; andgetFirstSearchTokenistokenize(value)[0], stored as an indexed column and used as a range-scan bound, so a skill namedデータベース管理indexed under the single characterデand the bound stopped being selective. The sibling reference sits 53 lines below in the same file:detectCJKLanguagecounts katakana with the full Unicode block/[゠-ヿ]/, which does matchー— that is how the text gets routed to the Japanese segmenter in the first place — so the pre-split was excluding the character the language detector had just counted, and the file disagreed with itself. Widening to that full block was tried and rejected in the PR body rather than left for a reviewer to ask about: it produces identicaltokenize()output, but leaks・,゠andヿinto thesegmentCJKByCharfallback as standalone tokens, so the change stayed at two characters. Review found the half I had missed, and it was mine to own — changing a tokenizer stales persisted data, because those first-token columns are recomputed only when a skill is written, and the mirrored skills.sh catalog carries its own copy of the same keys through a duplicatedfirstSearchTokenhelper the native backfill could never reach. Both tables got a cursor-paginated, rate-spaced, idempotent backfill, the duplicated rule was collapsed into one exported helper so the two copies cannot drift apart again, and both backfills now preview by default behind a per-path confirm token that rides the scheduled continuation — the first shape defaulteddryRuntofalse, sonpx convex run maintenance:backfillSkillSearchDigestFirstTokens --prod, a command that reads like an inspection, would have rewritten a reactively subscribed table and scheduled every remaining page. Proven against a real Convex backend rather than a mock: an anonymous local deployment seeded while the base commit was deployed so its digest rows carry the old tokenizer's output, then this branch deployed over them — 11 of 13 rows drift, an unconfirmed apply and an apply carrying the other path's token both throw before the firstpaginateand write nothing, and one confirmed page of ten leaves the eleventh row stale for the whole 15 s the continuation waits before it lands, which is what measures that the token survived the scheduler rather than just that the paging works (PR #3363) - Chinese, Japanese and Korean catalog cards rendered previews three or four characters long — on every surface that goes through
truncateText: the home listing sections, the skills and plugins catalogs, search results, publisher cards and the dashboard. The helper slices to the budget and then backtracks to the last space so a preview never ends mid-word, a rule that assumes spaces mark word boundaries throughout the text. In non-spacing scripts they usually do not, and the only space in an entire summary often sits immediately after an opening Latin token — a product name, a protocol, a version number — so the backtrack rewound past everything else. The measurement came out of the repository's ownfixtures/public-corpus/corpus.jsonlrather than constructed strings: 25 real catalog entries improved, the worst of themhuangli-query-cnrendering as the single character|at an 80-character budget and5gc-automationas5GC…, while all 1362 space-separated previews in that corpus came out byte-identical — so the behaviour English cards rely on is measured rather than asserted. The rule is made conditional rather than removed, and review was right that my first condition was too broad: a kept-ratio threshold applied to every script loses the boundary on a space-separated summary that happens to end in a long token such as a URL. The fallback now also requires that what is being discarded is actually non-spacing script, tested with the character class the catalog search tokenizer already defines (CJK_REinconvex/lib/searchText.ts, character for character) rather than one invented for this fix — and both halves are shown load-bearing by mutation, since dropping either turns exactly one test red. The real-browser proof took two rounds, and the lesson was the baseline: the first capture compared the reviewed head against the branch, which shows the review fix but not the repair the PR is about, and the bot refused it correctly. The second ran Chromium overbun run previewagainst the same public Convex deployment the repository's own Playwright job uses, compared againstmain, and read back three live CJK cards going from 25, 29 and 38 rendered characters to the full 80-character budget with no Latin preview differing between the two arms (PR #3362) - A fully documented skill was rejected at publish as "too thin or templated" whenever its SKILL.md carried no YAML frontmatter and used
---as an ordinary Markdown horizontal rule — the skill never reached the catalog, and the newest accounts felt it first, since the reject floor is highest for the lowest trust tier. The quality gate stripped frontmatter with anm-flagged pattern, so^matched at every line start rather than only the start of the document: it latched onto the first thematic break and deleted everything up to the second. Frontmatter is optional on publish — the display name comes from the mutation's own arguments — so that document is legal input. 94% of the body was being discarded before measurement: a 109-word SKILL.md measured as 6 words,score30 → 100,decisionreject→pass; the truncated text also fed the template-spam fingerprint, so similarity was being compared over a fragment. Anchoring the pattern to the document start is what the canonical parser and the repo's three other frontmatter patterns already did, pinned by the first unit tests for a module that had none — two of the three fail on the parent commit. The maintainer's follow-up carried it further and routed the gate through that shared parser, deleting the fourth regex outright (PR #3297) - A publisher who swapped a file after the first changelog preview landed submitted a changelog describing a bundle they were no longer publishing — on the skill update form the generated "What changed" text is cached in a ref keyed on the slug, version, SKILL.md size and
lastModified, and the path count, while the path list itself is what the action receives asfilePaths: exchange one bundled script for another and the key is identical, so the second request is skipped. Nothing reset that key when the selection changed, and the generation effect returns early while the field is non-empty — so once a preview had landed, no later change to the file set could replace it for the rest of the session. The sibling plugin publish form already covered both halves, keying onnormalizedPaths.join("\0")and resetting the cached key when the file set changes; the skill form now agrees with it, and the reset returns early on a changelog the publisher typed, so manual text is never discarded. Read out of the mounted form with the sameSKILL.mdinstance reused so only a sibling file differs: 1 preview call carrying the supersededfilePathsbefore, 2 calls ending on the current bundle after (PR #3296) - A listing untouched for just under a year read "Updated 12mo ago" instead of "1y ago" across skill rows, browse results, plugin detail, the dashboard and the GitHub sync timestamp:
timeAgomeasures months as 30 days but years as 365, and 360–364 days still divides into twelve whole months while sitting below the year threshold. Aligned with the publisher-profile renderer by deriving years from whole months, removing the unit mismatch at its source — with the first tests for a file that had none despite being inside the coverageincludelist (PR #3174)
openclaw/mcporter
· 2 merged · 1 co-authored
- A server that was simply unreachable was reported as needing authorization — a browser OAuth flow launched at the user and the stored server definition promoted to
auth: 'oauth'— whenever the connection error's text happened to contain the digits401anywhere: a port, a timeout duration, a hostname, a request id. The classifier's auth check was a rawincludes('401')with no word boundary, and it ran before both the generic HTTP branch and the offline-transport branch, so a message that plainly matchedECONNREFUSEDstill came backauthand the real fault the user had to fix was hidden behind an authorization prompt. The control case is one character wide:ECONNREFUSED 127.0.0.1:9000classifiesoffline,127.0.0.1:14012classifiesauth— the committed offline test passed only because its port happened to be lucky. The intended precedence was already written down in the repository's own tests (code=404 as http (not auth),code=500 as http,405 as transport/http instead of auth) and in two earlier fixes pointing the same way; the implementation honoured it only while the message carried no auth-like text. Fixed as an ordering rather than a special case — a known status code decides first, then the unambiguous keyword signals (unauthorized,invalid_token,forbidden), thenOFFLINE_PATTERNS, and only then the bare401numeral, the one signal ambiguous enough to belong below transport evidence. That split is also what closed the review's remaining merge risk in code instead of asking for it to be accepted: demoting every auth signal below the offline check would have sent a genuineinvalid_tokenpayload that also says "connection timed out" tooffline, so only the numeral moved, and the compatibility change is confined to exactly the reported defect. The word boundary itself took two passes — excluding neighbouring digits still letrequest_401_idandabc401defthrough, while\bwould have brokenunauthorized_client, the RFC 6749 error code — so the numeral is bounded on alphanumerics and_while the keywords stay substrings. Proven through the realisUnauthorizedError→maybeEnableOAuthpath with no mocks, no injected transports and no network: three transport failures that promote tooauthonmainstay unpromoted here, with the lucky-port case as the control, and every added regression shown load-bearing by restoring the previous ordering and watching exactly the intended tests go red and no others. The maintainer merged it with the ambiguous-numeral precedence accepted as implemented (PR #248) - A headless deployment that already held OAuth credentials had no way to install them —
mcporter vault setrejected everyclientInfofield that was not a string, so no real RFC 7591 dynamic client registration response could be seeded:redirect_uris,grant_types,response_typesandcontactsare string arrays, and both registration timestamps are numbers. The declared type said otherwise —VaultPayload.clientInfoisOAuthClientInformationMixed, mcporter builds that same array-valued shape itself during registration and readsclientInfo.redirect_urisback as an array — so its own client information could not round-trip through its own command. One rule for every field, whilevalidateOAuthTokensdirectly above it already typed each of its own fields individually. Two adjacent defects fell out of writing the replacement table, and they are the part that outlived my patch: the unguardedJSON.parseon the credential payload surfaced V8's parser message, which quotes a prefix of its input, so a malformed token file printed a fragment of the token itself through the unexpected-error path and into whatever log was collecting it; andtokens.expires_at/expiresAtwere never checked at the write boundary although the read guardisStoredOAuthTokensrequires finite numbers, so a typo in one field made the command printSaved OAuth credentialswhileloadVaultEntryhanded back an entry carrying neither tokens nor client information. That silent loss is only visible if the proof is taken through the read path — readingcredentials.jsondirectly bypasses the guards and reports everything present. My own pre-push review caught a regression of the same class inside my own fix: deriving the table from the SDK schema droppedissuer, an mcporter extension the old string-only rule had been covering by accident, whose sole consumer is the refresh-time issuer pin. The maintainer landed his own narrower fix for the reported defect (PR #288), keeping the partial and null-compatible client information that my table's requiredclient_idwould have started rejecting — a compatibility change I had flagged in my own body along with the exact three lines to drop it — then closed mine as superseded while explicitly preserving both adjacent findings, and shipped them as PR #294, crediting me as co-author on the merge commit (d7330dd) and in the changelog (PR #287, issue #286) - The one place a headless operator looks before writing an OAuth credential payload described a narrower shape than the command accepts —
mcporter vault set --helpstill presentedclientInfoasclient_idalone, while the validator had since been widened to take a full RFC 7591 dynamic client registration response: theredirect_uris,grant_types,response_typesandcontactsarrays, theclient_id_issued_at/client_secret_expires_attimestamps, and provider metadata outside the spec. That gap is not hypothetical — issue #286 was filed with exactly the expectation the old line creates, and the reporter's payload was a full registration response.docs/config.mdnames no fields at all, so nothing else in the project contradicted the help text either way and the command's own output was the only surface that could be wrong. Four lines appended underPayload:, with the existing one-liner left first so the shortest usable payload still reads first. The wording deliberately names field groups rather than restating the rule table:OAUTH_CLIENT_STRING_FIELDSalone is 15 entries, and a help text that enumerates a validator is a second copy free to drift from it. "Provider metadata outside RFC 7591" is the one behaviour a reader cannot infer from the spec —validateOAuthClientInfoiterates its own field lists rather than the payload, soregistration_client_uriandregistration_access_tokenreach the vault untouched. Proof is a before/after transcript of the real CLI rather than a rendered string, and the regression test is the first help coveragevaulthas ever had — built in the shape of the existingcli-auth-helptest, so it also pins theUsage:line and the exit code the help shortcut sets; reverting only the source file tomainturns it red (PR #302)
openclaw/fs-safe
· 4 merged · 1 superseded
- A file outside a confined root was reported as inside it whenever the root string carried surrounding whitespace —
isPathInside("C:\root ", "C:\root\secret.txt")returnedtrue.isPathInsideis the predicate the other guards build on, and onwin32its only normalization step ended by delegating to a free-text string coercion helper — the same module that normalizes fast-mode flags and thread values — whose chain callsvalue.trim(). So a path used for containment math was trimmed before it was lowercased, and since whitespace is a legal part of a Windows path component, two genuinely different directories collapsed onto one comparison key. Fixed by lowercasing in place instead of routing the path through that helper, leaving separator and extended-length handling untouched. Unicode case folding is deliberately left alone and the reason is written into the PR:toLowerCase()is not injective — on a Turkish-language Windows install"İstanbul"folds to a 9-code-point string that never round-trips — so moving to an ASCII-only or locale-invariant fold changes behavior for every non-ASCII path and reads as an owner decision rather than a bug fix. The review asked for the intended contract to be owner-approved; it turned out the repository had already written it down, in two committed tests carryingskipIf(skipOnWindows). Removing that skip made the measurement possible and showed the deny list onmainapplying to the wrong directory — the protected directory writable while its sibling was blocked. Verified on Windows 11 with Node 24.15.0 through the real exported functions, with the new tests proven load-bearing by stashing onlysrc/path.ts: 2 failed | 3 passed before, 5 passed after (PR #78) - A path spelled
C:secret.txtread and wrote a different file than the one it named — on Windows it aliased ontosecret.txtat the root of a confined store, so two distinct untrusted keys resolved to one file.path.win32.isAbsolute("C:secret.txt")returnsfalsefor the drive-relative spelling — no separator follows the colon — whilepath.resolve()still consumes the drive prefix, so every layer that screens for absolute paths waved it through and the prefix vanished one call later. The escape is not the interesting part; the aliasing is, because the guard that catches escapes (isPathInside) sees only the already-collapsed result and correctly reports the file as in-root. Review pushed the fix down two layers, and both times the reviewer's location was one level off from where the hole actually was: the file-store parser was never onRoot's path at all (assertValidRootRelativePath()was a NUL check), and thenreadAbsolute()/reader()turned out to resolve the raw input before validating it. Proven on Windows at each step rather than argued —root.read("C:secret.txt")returning the realsecret.txt, with a resolution table for the four spellings and alogs/2026-08-02T10:30:00Z.logcontrol proving the anchored pattern does not eat timestamped names. I argued against gating the guard onprocess.platform, on the grounds that a key valid on Linux must not become a boundary violation when a store moves between hosts, and that was kept. The maintainer narrowed the blast radius before landing: applied where a path is created or resolved — writes,mkdir,copyIn,resolve(), everyFileStorekey, and the destination ofmove()— but not to reads,stat,listor the source ofmove(), sincec:notes.txtis a legal POSIX filename and refusing to read back a file that already exists on disk is collateral, not containment (PR #85, landed as #97) - Concurrent lock acquisition failed intermittently on Windows against a lock file that no longer existed — and because the failure had been read as CI noise for four releases, the repository's own
mainhad been red since a dependency refresh, with a different concurrency test failing nearly every run. Windows denies access to a file whose directory entry is still being torn down, so a contendedacquireFileLock()gotEPERMon a name already gone;acquire()treated onlyEEXISTas contention, so the transient denial escaped from both the exclusive create and the holder's snapshot read. Instrumented at the moment of failure,lstatreportedENOENTand a zero-delay retry opened the file. The evidence had to be a distribution, not a green run — 8 failures in 85 runs before, 0 in 110 after — because a single pass proves nothing about a race. Two review rounds raised the same P1 and were right both times: scoping a retry to a code region keeps leaking, because the region always holds more than the operation you measured (first the caller'spayload()callback, then a parent-directory open hidden inside the native create). Retrying is not neutral when the retried block can re-run a caller's callback, so the predicate that finally held names the evidence instead —EPERMand the exact lock pathname — and anything unproven propagates. The maintainer closed the last gap himself: the Windows native binding reportedERROR_ACCESS_DENIEDas pathlessEACCES, so on packaged installs the predicate could never match, invisible here because every test in the file forces native mode off (PR #87, landed as #92) - Every
Root.remove()failure was reported as a containment violation — a missing file, a non-empty directory and a busy handle all threwpath-alias/ "path is not under root", so a consumer could not separate a routine filesystem outcome from a safety rejection, and downstream code inopenclawhad grown a helper purely to unwrap the real errno back out of the bogus one.not-found,not-emptyandnot-removablewere declared in the exported error union and promised in six documentation sites, but constructed nowhere insrc/: the remove path funnelled every non-FsSafeErrorthrough a normalizer whose default ispath-alias. Framing decided the PR — the documentation was already correct, which puts this on the contract-repair side of the line this repository merges on, rather than the contract-change side where its one rejected external PR sits. Review then found a genuine defect in my first fix: the errno mapping wrapped the whole fallback including the parent-directory guard, so a rawELOOPsurfaced asnot-removablewith nothing deleted; the second push splits the guard into its own stage so only the deletion syscalls are classified, and the guard fails closed. The maintainer added the half I had missed — all three codes were also absent fromOPERATIONAL_CODES, socategorizeFsSafeError()kept labelling themcategory: "policy", and fixing the code without the category would have delivered half the change (PR #84, landed as #93) - Diagnosed why two Windows permission tests kept timing out in CI, and flagged the unbounded subprocess call underneath it — the
Node N checkjob never builds the native binding, so those tests take the command fallback and pay six process spawns each, two per inspection (powershell.exefor the owner query,icacls.exefor the ACL). I opened the timeout increase as an explicitly test-only PR and kept the real finding out of it:defaultPermissionExeccalledexecFileAsyncwith notimeout, so a wedgedicacls.exewould hanginspectPathPermissions()indefinitely for any consumer — a public behaviour change that did not belong smuggled into a budget bump. The maintainer took the diagnosis and not the number, which was the better outcome: rather than widening the budget to fit the cost, PR #89 removes a redundant inspection so the affected tests drop to four spawns and ordinary Windows CI keeps exercising the command fallback, and it bounds the commands with a fail-closed result — an owner query that cannot complete now yieldssource: "unknown"andreadSecureFile()refuses withpermission-unverifiedinstead of returning a permissive answer. The tests went from 15-second timeouts to about four seconds (PR #88, superseded by #89)






