Utkarsh Bahuguna, reliability engineer and adversarial tester, Bengaluru

Utkarsh Bahuguna, Bengaluru, India

UtkarshBahuguna
SystemsAdversary!
A dark terminal card. A shell prompt reads uname -a on a Linux reliability lab machine, above a focus list of systems reliability, adversarial testing, distributed consensus and open source. Below it an architecture diagram runs from clients through a gateway to a matcher core, an order book on a CRDT replica set, a risk engine and persistence. At the foot, shadow reliability metrics and a p99 latency chart.

A computer science undergraduate at BITS Pilani working on systems that have to be right rather than merely finished. Order books, kernels, CRDTs, inference engines. In every case the interesting half of the work is the machinery built to prove the first half wrong.

Illustrated portrait cropped close on the eyes, behind clear framed glasses
  • reliability engineer
  • adversarial tester
  • open source contributor
Harness
A perforated postage stamp: an orange crab holding a small ball marked GC, above the printed lines NAME, Utkarsh Bahuguna, and DATE, 2005/02.

Upcoming Next

Live work, not
shipped work. Open
in front of me now.

tip!Click on the image to explore

Printed plate: seven measurement intervals stacked in rows, each a capped line with a dot at its centre, drifting further from a vertical reference rule and widening as they go, three of them in red, inside a ruled box with a tick scale along its foot.DivergenceIn progressReasoning-model behaviour · ICLR 2027One prompt, one set of weights, sampled traces that reach incompatible conclusions.
Printed plate in two panels. Above, one inked node at the left with twelve lines fanning out to separate dots, the red ones at the top and bottom extremes. Below, a histogram of narrow bars, taller at both ends than in the middle, a few of them red.

Build, Break Prove

Build, BreakProve

I build the system first, then the thing whose only job is to prove it wrong. The harness is not scaffolding around the deliverable, it is the deliverable: an engine nobody has tried to falsify is not one anybody should trust. The claim is worth exactly what the adversary failed to break.

All Work

Merged contributions

Contribution indexMicrosoftNVIDIAHugging FaceCNCFGCC

Research

Published work, and what is currently open in front of me.

  • When Self-Consistency Backfires

    arXiv:2608.11403 · Accepted at the COLM 2026 Workshop on Efficient Reasoning

    Accepted

    The standard way to make a language model more reliable is to sample several reasoning traces and take the most common answer. On hard science questions, that makes it worse more often than it helps. Across 198 GPQA Diamond problems, majority voting reduced accuracy on 56.6% of problems for one model and 65.7% for another. The obvious response is to detect the bad cases and route around them, so I tested three cheap confidence signals that could do that. All three fail, each for a different reason, and one of those failures I still cannot explain. Every hypothesis was registered and git-tagged before the confirmatory data was touched. Version two revised the mechanism claim in my own accepted paper rather than defending it.

  • The follow-up

    No preprint yet

    In progress

    The unexplained failure above is what I am working on now. The question is whether agreement between samples measures evidence at all, or just measures how strongly the model already believed the answer before it reasoned. To test it I needed reasoning-model data that the published study could not afford to buy, so I generated it myself: 12,672 samples across 198 problems, 82.7 million tokens, twenty-three hours on a consumer laptop GPU. The hypotheses and kill conditions are written down and tagged before anything is run, so the result is whatever it is. No conclusion yet, and possibly no paper. That is the arrangement.

Same seed, same crash

Printed plate captioned CNCF, jaeger-ui: a trace waterfall of nested spans as stacked bars, one of them in red where a span link was read back as a parent and closed the trace into a cycle.
Same
Printed plate captioned GCC, gccrs: a parse tree branching down five levels, with one path and its leaf in red, the error sentinel the parser returned with no diagnostic.
seed,
Printed plate captioned NVIDIA, garak: a probe and detector matrix, cells filled where an attempt got through and two of them in red.
same
crash

A bug that only appears sometimes has not been found; it has been noticed. Every system here is built so that a failure carries a seed, and the seed replays the failure on any machine, in any order, as many times as you want.

That is the whole discipline: make the machine deterministic enough that its bugs can be handed to somebody else. contribution index.

  • NVIDIA
  • CNCF
  • Jaeger
  • Hugging Face

What broke

tip!Hover, focus or swipe to read

Open source

Merged!

68 merged · 12 repositories · 11 organisations

Ordered by the name
you would recognise,
not by the date.

tip!Expand an organisation for its pull requests

  • Upstream GCCThe gccrs fix for types used as generic arguments was sent to gcc-patches as part of a 77 patch sync and committed to GCC master, with authorship preserved. It is in the compiler.
  • Bugs I foundThirty-eight of these fix crashes and defects I found and filed myself rather than picking up an assigned ticket: thirty-two in PyRIT, five in gccrs, one in Jaeger. One of them was a bug in my own already merged code.
    • PR #1960issue I openedBuilt the CodeAttack jailbreak converter, encoding an objective into a code-completion template across five language variants.

      The CodeAttack technique from Ren et al., across five language variants. Two and a half months and 43 review comments, with the tests growing from 31 to 88. I argued for a separate attack class; the maintainer decided otherwise, so it landed as a converter.

      Open PR #1960 on Microsoft PyRIT
    • PR #2721issue I openedAdded the code_attack_framed technique, a system-framed variant of the CodeAttack converter, leaving the paper-aligned original untouched.

      The paper defines no system prompt, so this framing is a PyRIT-specific condition rather than part of the method: opt-in catalog only, default technique set untouched. The generic mechanism the issue asked about already existed, so no new abstraction was added.

      Open PR #2721 on Microsoft PyRIT
    • PR #2822issue I openedFixed TAP and PAIR skipping their first real turn whenever a conversation had been prepended.

      Prepended messages counted towards the turn count, so the first real turn was treated as already taken: the adversarial system prompt was never set and a required message was dropped. The node now tracks how many it persisted. Five end-to-end cases fail on main.

      Open PR #2822 on Microsoft PyRIT
    • PR #2816issue I openedStopped MultiPromptSendingAttack mutating the caller’s own messages, and carried harm categories through as the base class already did.

      The attack sent the caller’s message objects rather than copies, so reusing them across runs, or broadcasting through AttackExecutor, modified the caller’s own data and failed on a unique constraint. It now sends a duplicate. Three tests, all failing on main.

      Open PR #2816 on Microsoft PyRIT
    • PR #2811issue I openedMade seed removal list filters match whole elements instead of JSON substrings, which had been deleting more than was asked.

      Under the default exact mode the list filters matched substrings of the stored JSON, so a value could match inside an unrelated element and take it too. They now use the whole-element helper already used elsewhere, on both SQLite and Azure SQL.

      Open PR #2811 on Microsoft PyRIT
    • PR #2810issue I openedFixed the JBB-Behaviors loader seeding every run with a short label instead of the actual request.

      The loader read Behavior, a short label, where it should have read Goal, the request itself, so every seed was a stub. The test mocks did not match the real schema and were hiding it. Checked against the live dataset: 100 full requests.

      Open PR #2810 on Microsoft PyRIT
    • PR #2814issue I openedStopped the HTTP targets storing the printed form of a bytes object, and taught the JSON key parser about digits and hyphens.

      Both targets stored the printed form of the raw bytes, so a saved response was a bytes repr and any non-ASCII body came back mangled. They now store decoded text. The JSON key parser also rejected digits and hyphens, leaving output2 and generated-text unaddressable.

      Open PR #2814 on Microsoft PyRIT
    • PR #2812issue I openedMade the scale scorers retry an out-of-range judge score through the existing JSON retry instead of crashing on it.

      A judge answering outside the scale, or with something non-numeric, took the scorer down. An out-of-range value now raises the same exception the existing JSON retry already handles. Scorer identifiers are untouched, so existing evaluation hashes still match.

      Open PR #2812 on Microsoft PyRIT
    • PR #2687Consolidated two copies of the async image download path behind one shared helper, with no public API change.

      Two image converters each carried their own copy of the same download. It moved behind a shared helper while the protected hook stayed on both classes, so subclasses that override or patch it keep working. Error handling and cancellation were preserved rather than rewritten.

      Open PR #2687 on Microsoft PyRIT
    • PR #2818Kept the raw HarmBench behaviour in metadata, and gave the context separator a name instead of a bare literal.

      While this was open the main fix landed in a maintainer’s own pull request, so what remained was two review points: the separator became a named constant, and every row keeps its raw behaviour in metadata, which is now the only place that text survives.

      Open PR #2818 on Microsoft PyRIT
    • PR #2820issue I openedMoved the scenario success calculation into one place, so the SDK, the backend and the reports stop deriving it three ways.

      The same statistic was computed independently in three layers, which is how they came to disagree. Plan lookup, execution-unit identity, latest-attempt selection, denominators and rounding now live in one module and everything else presents its result, with errors and retries reported separately from the effective counts.

      Open PR #2820 on Microsoft PyRIT
    • PR #2897issue I openedMade the general self-ask scorers keep the configured category rather than failing when the judge returned one as well.

      With their default handlers both scorers failed outright when the configuration and the judge response each carried a category. The configured one now wins, and the judge’s is used only when none was configured. That deliberately replaces the documented response-first contract, so the conflict behaviour is a named policy rather than a quiet default.

      Open PR #2897 on Microsoft PyRIT
    • PR #2895issue I openedStopped an inverted or combined score carrying a float that described a verdict it no longer had.

      The inverter kept the wrapped threshold float, which describes the uninverted verdict, so a normalised score could contradict the verdict printed beside it. The float is dropped when a score is inverted or when several are combined, and kept for a single score where it still matches.

      Open PR #2895 on Microsoft PyRIT
    • PR #3017issue I openedDeduplicated three dataset loaders that were returning the same prompt many times over.

      Each loader now keeps one seed per prompt and merges the harm categories from every row carrying it, so a filter still only admits the categories of rows that pass. On the real data BeaverTails falls from 166,382 seeds to 14,402, PKU-SafeRLHF from 73,907 to 38,641 and Aegis from 19,093 to 13,246.

      Open PR #3017 on Microsoft PyRIT
    • PR #3019issue I openedStripped the instruction template out of ALERT prompts so the seed is the prompt, and carried the attack type into metadata.

      The loader shipped each row with its Instruction and Response scaffolding attached, so every seed carried a template the target was never meant to see. Rows without the template are left alone, and the attack type now reaches metadata when the row has one.

      Open PR #3019 on Microsoft PyRIT
    • PR #3021issue I openedStopped two scenarios silently dropping the technique converters a caller had asked for.

      Doctor and AdversarialBenchmark never passed technique_converters to the matrix builder, so they applied nothing and said nothing. They pass them now, and the scenarios that genuinely cannot apply them declare it instead of ignoring the argument.

      Open PR #3021 on Microsoft PyRIT
    • PR #3080issue I openedKept Responses API web search calls replayable, instead of storing only enough of one to break the next turn.

      Parsing kept a call’s type and id and discarded its status and action, so replaying the conversation sent back an item the API would not accept. The whole item is stored now, and rows written before the fix still replay.

      Open PR #3080 on Microsoft PyRIT
    • PR #3082issue I openedMade the runner reject dataset limits that the chosen scenario would have quietly ignored.

      Exploitation scenarios run a fixed dataset, so a dataset name, filter or size limit passed alongside one was accepted and then discarded. Configuration resolution now checks what the scenario takes and says plainly that the dataset is fixed.

      Open PR #3082 on Microsoft PyRIT
    • PR #3084issue I openedCorrected the Multilingual run size estimate, which ignored the language axis and came out three and a half times low.

      The scenario multiplies each seed group across languages, and the estimate used the base formula, which counts none of that. It now mirrors what the scenario builds, so the number shown before a run matches the run.

      Open PR #3084 on Microsoft PyRIT
    • PR #3086issue I openedMade a Psychosocial dataset subset narrow the run, rather than leaving sub-harms whose data had not been selected.

      Picking a subset of datasets still ran every sub-harm, including the ones whose dataset was absent. A single selector now gates validation, seed resolution, the size estimate and the attack build, so the four cannot disagree.

      Open PR #3086 on Microsoft PyRIT
    • PR #3090issue I openedMade the UTCDateTime column store aware datetimes as UTC, so filtering by time stopped depending on the caller’s zone.

      Naive values were read as UTC and aware ones stored as written, so a timestamp from any other zone was compared against UTC rows and matched the wrong ones. Conversion happens on the way in now, and the schema is unchanged.

      Open PR #3090 on Microsoft PyRIT
    • PR #3092issue I openedIncluded the extra body parameters in chat target identifiers, so two differently configured targets stop sharing one identity.

      The Responses target already did this. On the chat and LiteLLM targets the extra body was left out, and since the audio configuration is merged into it, two different voices were recorded as the same target.

      Open PR #3092 on Microsoft PyRIT
    • PR #3094issue I openedGave adaptive technique selection the run id on a fresh run, not only on a resumed one.

      The id existed only once a result had been stored, so on a first run the selector had nothing to key its history to. The context now carries the id the result will take, chosen before the attacks are built.

      Open PR #3094 on Microsoft PyRIT
    • PR #3097issue I openedStamped the attack identity on error results, which until then came back anonymous.

      The error handler is a separate object with no view of the strategy, so a failed attack produced a result that could not be traced back to what produced it. The context now carries a small builder the handler stamps from.

      Open PR #3097 on Microsoft PyRIT
    • PR #3100issue I openedMade the Prompt Shield scorer read the converted prompt, as every other scorer does, rather than the original.

      One line, with a large consequence: any attack carrying a converter was scored on text the target never saw. Unconverted prompts give the same value either way, which is why this went unnoticed.

      Open PR #3100 on Microsoft PyRIT
    • PR #3102issue I openedKept the original float value readable at the top level of a fallback score, where callers look for it.

      The composite scorer wrote the selected child’s value only under a primary or fallback prefix, so anything reading the standard key off the result got nothing. The prefixed copies stay, so nothing already working changed.

      Open PR #3102 on Microsoft PyRIT
    • PR #3104issue I openedMade TAP branch on the objective score rather than on whichever auxiliary scorer answered first.

      The node took the first score it found, so adding any second scorer steered the tree search by the wrong signal. It now matches on the objective scorer’s identity, and reports the score as unavailable when there is none.

      Open PR #3104 on Microsoft PyRIT
    • PR #3107issue I openedHandled grayscale and palette images in the rotation and saturation converters, which assumed every input was RGB.

      The fill colour on a rotation is RGBA and saturation works in RGB, so a palette PNG or a grayscale photograph raised instead of converting. Inputs are promoted first now, to RGBA where they carry transparency and RGB otherwise.

      Open PR #3107 on Microsoft PyRIT
    • PR #3109issue I openedKept int32 and uint8 WAV input intact through the audio frequency converter.

      The converter assumed signed 16-bit samples, so other integer formats came out as noise. It shifts around 128 for unsigned input, keeps the input dtype and clips integer output to its range, the approach an earlier fix took for volume and echo.

      Open PR #3109 on Microsoft PyRIT
    • PR #3111issue I openedCarried the memory labels onto the sequential attack result, which was the one attack dropping them.

      Every other attack passes the context labels through, and so does this attack’s own error path. The completed result did not, so a successful sequential run could not be filtered by the labels it was launched with.

      Open PR #3111 on Microsoft PyRIT
    • PR #3136issue I openedMade the PDF converter apply the font colour it was given, having accepted and discarded it until then.

      Generated PDFs were always black. The colour defaults to unset now and the generator fills with it when there is one, so anyone who never passed a colour sees exactly what they saw before.

      Open PR #3136 on Microsoft PyRIT
    • PR #3169issue I openedRecorded an unscored attempt as undetermined rather than as a failure.

      With an objective scorer configured, a response in hand and no score returned, the attack was filed as a failure, which is a claim the run cannot support. The outcome now says the scorer returned no score.

      Open PR #3169 on Microsoft PyRIT
    • PR #3170issue I openedMade the analytics count an attack with no converters as having none, instead of as missing data.

      Identifiers omit an empty converter list, so a real run with no converters had no list, no edges and a null array, which the query read as unknown. The source falls back to an empty list now and those runs are counted.

      Open PR #3170 on Microsoft PyRIT
    • PR #3200issue I openedGave the regex, package hallucination and aggregator scorers identities that include the configuration deciding their results.

      Several scorer identities left out the configuration, so two differently configured scorers hashed alike and could pick up each other’s metrics. A regex scorer now folds in a digest of its patterns when they differ from the class default.

      Open PR #3200 on Microsoft PyRIT
    • PR #3078issue I openedStopped the Copilot and A2A targets silently losing conversation history between turns.

      Copilot opened a fresh session for a conversation that already had turns and sent only the last message. A2A dropped its context whenever the agent returned none. Both now carry their own id or fail loudly, and repeat-send checks that a target can replay before copying a conversation.

      Open PR #3078 on Microsoft PyRIT
    • PR #3171issue I openedGave targets with no model name their own analytics key, instead of collapsing every one of them into a single unlabelled group.

      Analytics keyed objective targets by model name, temperature and top_p, so the HTTP and Gandalf targets, which have no model name, all hashed alike. They fall back to a content hash with a readable label now, and a migration reruns the backfill for databases written under the old keys.

      Open PR #3171 on Microsoft PyRIT
    • PR #1308Stopped the benchmark runner handing child processes the parent’s own script path, which broke every way of starting it but one.

      The task runner re-invoked the entry point by the path it had been given, so starting tritonbench by absolute path, as a relative path, or from a wrapper script made every child benchmark process fail. Only the bare invocation worked.

      Open PR #1308 on Meta PyTorch tritonbench
    • PR #1842Added a generic suppressed_params mechanism to the Bedrock generator, fixing scans that Claude 4.x rejected outright.

      BedrockGenerator sent both temperature and topP, which Claude 4.x rejects, breaking scans on those models. The maintainer invited a generic mechanism rather than the narrow fix, so it became a parameter map with suppression checks. A competing narrower pull request was closed for this one.

      Open PR #1842 on NVIDIA garak
    • PR #742Replaced string matching with real URL parsing in the local-address check, closing an SSRF hole.

      The check decided whether a target was local by matching substrings in the URL, which is not a decision string matching can make. It now parses the URL and uses Python’s ipaddress module, with 15 tests covering what the old check got wrong.

      Open PR #742 on Hugging Face OpenEnv
    • PR #4053Classified GenAI tool spans against the OpenTelemetry semantic conventions, and stopped a bare call id counting as one.

      This started as a thousand-line GenAI Observability Mode for CNCF LFX. The maintainer rejected it as too large to review and it stalled. I rewrote it as a three-file, plus-76 minus-4 change, and that is what merged.

      Open PR #4053 on CNCF jaeger-ui
    • PR #4271issue I openedFixed the trace view re-deriving GenAI detection by hand instead of calling the shared detector, silently bypassing it.

      A bug in my own merged code. The trace page scanned raw attributes itself instead of calling the canonical detector, so improvements to that detector were skipped on the auto-activation path. Four-line fix, merged about two and a half hours after I reported it.

      Open PR #4271 on CNCF jaeger-ui
    • PR #4313Added image and audio previews to the GenAI view, so a multimodal prompt can be inspected without copying its URL into another tab.

      Markdown and pretty-printed JSON had already landed; rendering referenced media had not, so a prompt whose content was an image link showed as link text. A remote link is never fetched until the reader asks, and that choice is never persisted.

      Open PR #4313 on CNCF jaeger-ui
    • PR #4421Corrected five wire-format claims in RFC 0002 against a live v2.19.0 server, including a field name the document had wrong.

      Found by probing a running instance and diffing its responses against the document rather than by reading the code: four claims that do not match what the server returns, plus one behaviour it never documented. The operations endpoint returns spanKind, not span_kind.

      Open PR #4421 on CNCF jaeger-ui
    • PR #4731Marked types used only as generic arguments as live, fixing a dead-code lint that discarded them.

      The dead-code pass never marked a type live when it appeared only as a generic argument. Three existing tests asserted that wrong behaviour, so I corrected the tests. This is the one that reached upstream GCC, committed to master with authorship preserved.

      Open PR #4731 on GCC gccrs
    • PR #4728Emitted E0259 and E0260 for duplicate extern crate names, matching rustc’s diagnostics.

      The most involved review here. My first design stored extern-crate status on the resolver rib; the reviewer asked for a mapping keyed on node id instead. Rebuilding it surfaced an ordering difference: the duplicate check runs inside the insert, so status has to be recorded before it.

      Open PR #4728 on GCC gccrs
    • PR #4799issue I openedReported an error on an empty path expression instead of segfaulting the compiler.

      A crash I found myself: `let x = ::;` segfaulted in type checking, three layers below the parser that returned an error sentinel with no diagnostic. The sentinel was a zero-segment path, indistinguishable from a legitimate empty one the whole way down.

      Open PR #4799 on GCC gccrs
    • PR #4929Stopped an internal compiler error on Fn, FnMut and FnOnce bounds, which produce a path segment the type parameter builder did not handle.

      Fn, FnMut and FnOnce bounds produce a segment the type parameter builder treated as impossible, so the compiler aborted rather than reporting anything at all. Verified by hand across six spellings, with the full suite at 11,627 passes.

      Open PR #4929 on GCC gccrs
    • PR #4930Made constant evaluation diagnose unsigned arithmetic overflow, which the existing check was structurally unable to see.

      GCC only sets its overflow flag for signed operations, because unsigned wraparound is defined behaviour in C, so the evaluator was reading a flag that would never be set and unsigned overflow in a constant compiled silently. The check now runs before the operands are folded.

      Open PR #4930 on GCC gccrs
    • PR #4959issue I openedImplemented match arm guards, which were parsed and then silently ignored, so a guarded arm matched on its pattern alone.

      Guards were never type checked or compiled, so a guarded arm was taken whenever its pattern matched, whatever the condition said. A guard is now checked as a bool and falls through when false, and the unsafe and const checkers see guards too, so one cannot call an unsafe function from safe code.

      Open PR #4959 on GCC gccrs
    • PR #4958issue I openedMade call arguments evaluate left to right on x86, where the gimplifier had been following the target push order.

      Arguments were evaluated right to left because the gimplifier follows the order the target pushes them, which is not the order Rust specifies. The callee and the earlier arguments are now saved in order before the call, so side effects happen in the order the source wrote them.

      Open PR #4958 on GCC gccrs
    • PR #4953Caught type parameter defaults naming a later parameter, which the existing check let through.

      A default can only refer to parameters declared before it, but only a default naming its own parameter was caught, so a struct whose first default named a later one was accepted. It reports E0128 for those too now, leaving a trait’s implicit Self alone because that is always in scope.

      Open PR #4953 on GCC gccrs
    • PR #4974issue I openedFixed an internal compiler error on patterns nested inside a constructor with several fields.

      The match checker popped a constructor’s sub-patterns onto the back of the row while specialisation put their types at the front of the columns. With more than one field the two orders disagreed and the compiler crashed.

      Open PR #4974 on GCC gccrs
    • PR #4979issue I openedFixed an internal compiler error on a rest pattern inside an enum tuple variant.

      The rest pattern code could only reach the fields of a struct, so a pattern like E::A(x, ..) tripped an assert in the backend. It reads the variant’s fields through the payload union now, as the same pattern without the rest already did, and a test marked as a known crash compiles.

      Open PR #4979 on GCC gccrs
    • PR #4998Made an inline const block parse at the start of a statement, the way an unsafe block already does.

      The first of three gaps in inline const support that I filed. Const blocks parsed where an expression was expected but not in statement position. The other two, compile-time evaluation and locals leaking into scope, are open.

      Open PR #4998 on GCC gccrs
    • PR #2777Modernised the type hints across the utils module, part of the project-wide migration off legacy typing constructs.

      Part of the tracking issue migrating legacy typing constructs to built-in generics across the model-compression toolkit. A small diff, and a net-negative line count, which is the shape maintainers like.

      Open PR #2777 on vLLM llm-compressor
    • PR #2779Modernised the type hints across the modeling module, same migration.

      The companion to the utils change, under the same tracking issue. Also net-negative.

      Open PR #2779 on vLLM llm-compressor
    • PR #23276Stopped the Inspector crashing the moment a model output was not a tensor, on both the printed table and the plot.

      The metric helpers return None for a non-tensor output, and the print loop then formatted it, so comparing results died on a TypeError rather than reporting what it could. None now prints as not applicable and plots as zero, so the bar chart still renders, and the CLI no longer calls the comparison at all when there is no reference output to compare against.

      Open PR #23276 on PyTorch executorch
    • PR #23400Fixed the Inspector plot crashing on a non-finite metric, which is exactly what a perfect match produces.

      A follow-up to the earlier Inspector fix. Signal-to-noise is infinite when the output matches the reference exactly, and matplotlib refuses an axis limit that is not finite, so the plot failed precisely in the best case. Non-finite values now plot as zero while the label still shows the real value.

      Open PR #23400 on PyTorch executorch
    • PR #4965Split the run-tags environment variable before handing it to wandb, which had been reading it one character at a time.

      The variable went into wandb.init as a plain string, and wandb calls tuple() on it, so setting it to exp1,baseline produced a tag per character: e, x, p, 1, comma, b, and so on. It is now split on commas with empty entries dropped, and unset still means no tags. The new test stubs wandb and fails on main.

      Open PR #4965 on PyTorch torchtitan
    • PR #5042Added range checks to the learning rate scheduler config, which accepted values that quietly did the wrong thing.

      A negative minimum factor drove the learning rate below zero near the end of decay, measured at about minus a third of the base over ten steps, and a negative decay ratio simply switched decay off. Both are now rejected at construction, following the pattern of the checks that landed before it.

      Open PR #5042 on PyTorch torchtitan
    • PR #5041Fixed a rollout filter that recorded everything when it was asked to record nothing.

      The top end was sliced with a negative index, and at zero that is the whole list rather than none of it, so turning recording down turned it all the way up. It is sliced by position now, and a negative setting is rejected instead of silently duplicating rollouts.

      Open PR #5041 on PyTorch torchtitan
    • PR #5040Fixed asset download matching, which lowercased the pattern but not the filename, so any uppercase pattern matched nothing.

      The script’s own help text offers README.md as the example, and that downloaded nothing, as did LICENSE. The basename is lowercased before the comparison now. Files are still written under their real names; only the matching changed.

      Open PR #5040 on PyTorch torchtitan
    • PR #5288Made the comparison scripts resolve commits before checking anything out, so a relative reference still means what it meant at the start.

      Both scripts checked out the baseline before reading the test commit, so anything relative to the current checkout, a bare dot or HEAD~1, resolved against the baseline instead and the run silently compared the wrong pair.

      Open PR #5288 on PyTorch torchtitan
    • PR #5289Gave each child of a concatenated dataset its own seed, so repeating a dataset stopped drawing identical samples from every copy.

      The mixing config already offsets each child’s seed by its index, deliberately. Concatenation built every child with the same iteration policy and therefore the same seed, which is exactly wrong for the repeat-a-dataset case.

      Open PR #5289 on PyTorch torchtitan
    • PR #1016Made a sandbox manager test repeatable, and stopped a real claim failure surfacing as a nil pointer panic.

      The test panicked whenever it ran more than once: a fixed sandbox UID leaked a resource version expectation into the next run, and retry.OnError then turned the resulting deadline error into nil, so the assertion passed and the test dereferenced a nil sandbox.

      Open PR #1016 on OpenKruise agents
    • PR #200Made the forward-node-ip setting a per-gateway annotation instead of one flag for the whole cluster.

      A roadmap item for configuring this per node pool. The tunnel engine already decided it per gateway, so an annotation on the Gateway overrides the global flag and anything unrecognised falls back to it. Every agent reads the same annotation, so both ends of a tunnel agree on whether that gateway’s node IPs are routed.

      Open PR #200 on OpenYurt raven