DEV Community

Cover image for From Stumbling Into Open Source to 373 Merged PRs: A Developer's Transformation
ANIRUDDHA ADAK
ANIRUDDHA ADAK

Posted on

From Stumbling Into Open Source to 373 Merged PRs: A Developer's Transformation

Summer Bug Smash: Smash Stories Submission πŸ›πŸ›Ή

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.


"You do not find your voice by speaking perfectly. You find it by speaking, failing, adjusting, and speaking again."


The Beginning

I still remember my first open source contribution. I was terrified.

I had found a bug in a library I was using. A small thing, really. A function that returned the wrong type when given an empty input. I knew how to fix it. I had the code ready. But the idea of submitting a pull request to a project with thousands of stars felt like walking onto a stage in front of an audience that knew way more than I did.

What if my fix was wrong? What if the maintainers laughed? What if I embarrassed myself in public, forever, on the internet?

I am Aniruddha Adak, and this is the story of how I went from that terrified first-timer to someone who has landed 373 merged pull requests across the open source ecosystem. This is not a story about natural talent or genius. It is a story about showing up, making mistakes, learning from them, and gradually becoming the kind of developer who can walk into any codebase and make it better.

You can find my work on GitHub, visit my portfolio at aniruddha-adak.vercel.app, or connect with me on LinkedIn and X.


Chapter One: The Security Bug That Changed Everything

Setting the Scene

It was late. I was browsing the codebase of cognee, an AI memory infrastructure project. I was not looking for bugs. I was just trying to understand how the settings API worked so I could configure it for a project of my own.

Then I saw it.

The POST /api/v1/settings endpoint. No privilege check. No role verification. Just a straight update to global configuration.

My first thought was, "I must be reading this wrong." Surely there was middleware somewhere. Surely I was missing an authorization decorator on another file. I searched. I traced. I checked the test suite.

Nothing.

The Moment of Realization

This was a full authorization bypass. Any authenticated user could modify global settings. LLM API keys. Database connections. Authentication configuration. Everything.

I felt a strange mix of emotions. Excitement at having found something real. Fear at the implications. Responsibility to fix it properly.

The Fix

fix(security): restrict global settings and disable public registration #3115

Closes #3084

This PR addresses the security vulnerabilities reported in #3084:

  • Requires superuser privileges for POST /api/v1/settings to prevent global configuration takeover.
  • Fully masks LLM and VectorDB API keys in GET /api/v1/settings to prevent leaking key prefixes.
  • Adds a COGNEE_PUBLIC_REGISTRATION_ENABLED environment variable to allow administrators to disable public self-registration.

I used Antigravity, my agentic IDE powered by Google AI, to trace the full authorization flow. The AI mapped the middleware stack in seconds and confirmed the gap. It suggested the fix pattern used elsewhere in the codebase.

Two lines. require_superuser() decorator. That was all it took.

The PR was merged within 24 hours. The maintainers were grateful. No one laughed. No one criticized. They just said thank you.

What I Learned

Security bugs are not found by security experts. They are found by people who read code carefully. You do not need to be a penetration tester to spot a missing authorization check. You just need to pay attention and ask the right questions.

Also: the open source community is kinder than you think. Maintainers want help. They want your contributions. They are not waiting to criticize you. They are waiting to thank you.


Chapter Two: The Windows Bug That Taught Me Humility

The Report

A user opened an issue on openclaw. OS Error 3 on Windows. Vector database operations failing. The error message was cryptic. The path looked fine.

I am not a Windows developer. My daily driver is Linux. Windows bugs feel foreign to me, like troubleshooting a car when you usually ride a bicycle.

The Investigation

I fired up Antigravity and asked the AI about Windows path handling. The response was immediate and precise.

Windows has a 260-character path length limit in its legacy API. When you exceed it, you get Error 3. Even if the path exists. Even if the file is right there. The solution is the \\\\?\\ extended-length path prefix.

But here is what I did not know. LanceDB, which cognee uses under the hood, adds its own directory structure to the path. So a path that looks like 180 characters in Python becomes 280+ characters by the time it hits the Windows API.

The Reproduction

I set up a Windows VM. I reproduced the bug. I watched it fail exactly as described. There is something deeply satisfying about reproducing a bug. It transforms a theoretical problem into a concrete enemy you can fight.

The Fix

fix(lancedb): automatically prefix windows paths to resolve OS Error 3 for long paths (fixes #2941) #3123

Closes #2941

This PR automatically normalizes and prefixes absolute Windows paths for the vector_db_url when using local filesystem storage. This resolves OS Error 3 triggered by LanceDB subprocesses when generating long file paths for persisting vector data on Windows.

I wrote platform-specific logic. On Windows, normalize to absolute path, add the prefix. On other platforms, pass through unchanged.

The PR review taught me something important. A maintainer suggested I use pathlib for the path manipulation instead of string concatenation. They were right. The code was cleaner, more Pythonic, and less error-prone.

What I Learned

Your platform is not the only platform. As a Linux developer, it is easy to forget that most of the world uses Windows. Cross-platform bugs are not edge cases. They are the main case for millions of users.

Also: review feedback is a gift. That maintainer who suggested pathlib was not criticizing me. They were making me better. I now use pathlib everywhere.


Chapter Three: The Lock That Would Not Let Go

The Mystery

Error reports were coming in about unhandled exceptions during cleanup in cognee. The stack trace pointed to a lock release statement. But the lock was acquired successfully. How could releasing it fail?

I stared at the code for an hour.

lock = acquire_lock()
try:
    yield lock
finally:
    lock.release()
Enter fullscreen mode Exit fullscreen mode

It looks correct. It looks fine. But it is not.

The Revelation

If acquire_lock() raises an exception, the finally block still executes. But lock is in an invalid state. The release throws a second exception, masking the original problem.

This is a cleanup code bug. The hardest kind to find because you are looking at the main logic, not the cleanup. The finally block is supposed to be the safety net. But a broken safety net is worse than no net at all.

The Fix

Safely release lock in hold_lock context manager #7

fixes #3294. This PR ensures that hold_lock only attempts to release the lock if it was successfully acquired. We now initialize the lock variable to None, attempt to acquire the lock inside the try block, and verify that the lock is not None in the finally block before calling release_lock.

lock = None
try:
    lock = acquire_lock()
    yield lock
finally:
    if lock is not None:
        lock.release()
Enter fullscreen mode Exit fullscreen mode

The release only happens if acquisition succeeded. The original exception propagates cleanly. No masking. No ghost exceptions.

What I Learned

Always validate before cleaning up. The finally block is not magic. It is just code. And code can have bugs.

Also: simple patterns hide subtle bugs. The standard lock pattern looks correct at a glance. It takes careful reading and an understanding of exception semantics to see the flaw.


Chapter Four: The Tests That Lied

The Discovery

While working on openclaw, I found this pattern:

if (process.platform === 'win32') {
    return; // Skip on Windows
}
Enter fullscreen mode Exit fullscreen mode

In multiple test files. Symlink tests. Path tests. File operation tests. All skipped on Windows.

The comment said "Windows does not support symlinks." But I knew that was wrong. Windows has supported symlinks since 2007. Since before some developers reading this were born.

The Realization

These tests were not protecting Windows users. They were hiding Windows bugs. An entire category of potential issues was going completely undetected because of a decade-old assumption.

The Fix

test(browser): replace broad win32 skip with dynamic directory symlink check #90365

Related: #90275

What Problem This Solves

The output-directories.test.ts test had a broad, unconditional skip for win32 platforms, meaning symlink rejection wasn't fully tested on Windows machines that do support symlinks/junctions. Additionally, the initial symlink capability probe was leaving uncleaned directories and failing linters.

Why This Change Was Made

To ensure that tests adapt dynamically to the environment's capabilities rather than blindly skipping based on OS. This makes the test suite more robust and accurate. The probe was updated to properly clean up after itself using fsSync.rmSync in a finally block and correctly evaluate if directory symlinks can be created on the given system without polluting the temp directory.

User Impact

No direct end-user impact. Improves test reliability and Windows developer experience by correctly evaluating symlink capabilities and ensuring no temporary directory pollution during testing.

Evidence

Tests run and pass successfully. All linters (including oxlint) pass on the updated probe.

βœ“  extension-browser  ../../extensions/browser/src/browser/output-directories.test.ts (2 tests) 270ms

 Test Files  1 passed (1)
      Tests  2 passed (2)

test: make install-safe-path symlink tests compatible with Windows #90275

Summary

  • Run the existing install-path symlink boundary tests on Windows when directory junctions are supported.
  • Use Windows junctions for directory links while preserving dir symlinks elsewhere.
  • Keep production install-path behavior unchanged.
  • Treat temporary-directory or cleanup failures in the capability probe as unsupported test environments instead of failing module import.

Linked context

No linked issue. This is a test portability improvement for existing install-path boundary coverage.

Real behavior proof

  • Behavior addressed: Three install-safe-path symlink boundary tests were unconditionally skipped on Windows.
  • Real environment tested: Native Windows Azure VM (Standard_D4ads_v6) through Crabbox.
  • Exact steps or command run after this patch: node scripts/run-vitest.mjs src/infra/install-safe-path.test.ts
  • Evidence after fix: Native Windows console output from Crabbox lease cbx_be4230e2069c, run run_0fb83e164185:
RUN  v4.1.8 C:/repo/openclaw

βœ“ infra src/infra/install-safe-path.test.ts (24 tests) 525ms

Test Files  1 passed (1)
Tests       24 passed (24)
  • Observed result after fix: The directory-junction cases executed successfully on native Windows instead of being skipped by platform.
  • What was not tested: No end-user install flow was exercised because the patch changes tests only.
  • Proof limitations or environment constraints: The tests still skip when the host cannot create directory links.
  • Before evidence: Current main uses it.runIf(process.platform !== "win32") for all three cases.

Tests and validation

  • node scripts/run-vitest.mjs src/infra/install-safe-path.test.ts
  • node scripts/run-oxlint.mjs src/infra/install-safe-path.test.ts
  • Native Windows Crabbox: 24/24 tests passed
  • Blacksmith Testbox tbx_01kv72nvfyz4fgpny8cyn48xfr: pnpm check:changed
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Risk checklist

  • Did user-visible behavior change? No
  • Did config, environment, or migration behavior change? No
  • Did security, auth, secrets, network, or tool execution behavior change? No
  • Highest-risk area: Windows directory-link capability detection in the test harness.
  • Mitigation: Capability-gated execution plus direct native-Windows proof.

Current review state

  • Next action: Refresh CI on the rebased head and merge when required checks pass.
  • Addressed review comments: Temporary directory creation and cleanup are contained by the probe; module-level probing remains intentional because Vitest evaluates skipIf during test declaration.

test: make qqbot symlinked media helper test robust on Windows #90223

Replaces the hardcoded Windows skip in the QQ Bot file-utils test with a dynamic file-symlink capability check. If file symlinks are supported by the environment, the test executes. Otherwise, it skips gracefully while keeping coverage active on capable hosts.

What Problem This Solves

The symlinked local-media helper test should reject symlinked media paths when the runtime can create file symlinks, but it should not fail the suite on Windows or restricted environments where file symlink creation is unavailable. Gating the test on actual capability avoids false negatives while preserving the security regression coverage where the behavior can be exercised.

Evidence

  • Windows Vitest proof from the contributor: extensions/qqbot/src/engine/utils/file-utils.test.ts completed with 1 passed test file, 4 passed tests, and 1 skipped symlink test when file symlink creation was unavailable.
  • The follow-up repair commit cb7d5a162e24f7ec5be6985e97b2b74ae45b20f9 changes the probe to async fs.promises APIs and skips solely on !canCreateFileSymlinks, which addresses the stale Copilot comments about non-Windows restricted environments and synchronous import-time filesystem work.
  • Current PR CI is otherwise green; the remaining failed check was the external-PR body proof gate requiring these authored sections.

I replaced platform checks with capability checks. Instead of asking "Are we on Windows?", I asked "Can this environment create symlinks?" If yes, run the test. If no, skip with a clear explanation.

For directories, I used Windows junctions, which have been supported since Windows 2000 and do not require admin rights.

What I Learned

Assumptions become invisible over time. That Windows skip was added years ago by a well-meaning developer. It made sense then. It does not make sense now. But no one questioned it because it had always been there.

Always question the assumptions. Especially the old ones. Especially the ones everyone accepts without thinking.


Chapter Five: The AI That Needed Better Training

The Context

opensre uses large language models to classify alerts and identify root causes in site reliability engineering. The promise is powerful. The implementation was flawed.

The Problem

Healthy alerts were being classified as noise. The LLM was filtering out scheduled maintenance checks and healthy status pings. Without these baseline signals, every alert looked like an emergency.

Engineers were getting paged for normal operations. Trust in the platform was eroding.

The Investigation

Using Google AI through Antigravity, I traced the classification pipeline. The issue was in the training data. The _build_database_directive() function lacked scenarios for:

  • Compositional faults where multiple issues mask each other
  • Red herrings in alert patterns
  • Dual fault symptoms versus single root causes
  • Missing storage metrics and organic inference
  • RDS-specific scenarios like connection exhaustion

The Fix

fix(synthetic-qa): Identify healthy alerts correctly (#596) #618

This PR fixes the 000-healthy synthetic-qa failure (Fixes: #596).

Cause:

  1. The LLM extraction step was classifying 'healthy' and scheduled checks (which have severity 'info' and state 'normal') as is_noise=True.
  2. Even if it bypassed noise extraction, the LLM was assuming it didn't need to gather investigation metrics because the alert explicitly said the database was normal, leading to an empty sequence of actions. This empty sequence caused the is_clearly_healthy function to loop infinitely because condition 4 requires at least one investigative operation.

Fix:

  • Noise Extraction: Updated app/nodes/extract_alert/extract.py prompt to explicitly state that informational states and health checks are NOT noise.
  • Planner Prompt Guidance: Updated app/nodes/plan_actions/build_prompt.py to ensure the agent MUST still query relevant monitoring platforms for verification when it identifies informational or healthy states.
  • Planner Code Guard: Added a code-level guard in app/nodes/plan_actions/node.py to fall back and force at least one verification action if the LLM returns an empty plan to prevent infinite insufficient_evidence loops.
  • Evidence Consistency: Fixed EKS evidence truthiness check in app/nodes/root_cause_diagnosis/evidence_checker.py to correctly evaluate via is not None.
  • Cleanup: Removed accidentally committed pr_body.md template.

fix: Database directives for RDS QA testing #625

Resolves #598 and #599 by supplying the agent with specific database directives that inform the RCA logic of standard scenarios like Connection Exhaustion and Free Storage exhaustion.

fix: Database logic expansion for QA Edge Cases (Batch 2) #626

_build_database_directive() has been expanded exponentially to train the AI to parse red herrings, distinguish between dual fault symptoms versus single root causes, infer missing Storage metrics organically, ignore healthy oscillating traffic metrics, and trace WAL replication lags adequately.

fix: Database logic expansion for QA Edge Cases (Batch 3) #627

Resolves #606, resolves #607, resolves #608, resolves #609, resolves #610. Expands the _build_database_directive() function to correctly train the LLM to identify Compositional Faults (treating simultaneous CPU and Storage constraints as independent sources while filtering out connection bounds), infer replication lag from bare WAL metrics despite missing Replica metrics, accurately ignore historical maintenance distractions via timestamps, identify stale autoscaling recovery, and distinguish VACUUM-driven Checkpoint Storms.

I expanded the directive system in three batched PRs. Each batch added new training scenarios. The LLM learned to distinguish healthy patterns from noise, handle complex edge cases, and reduce false positives.

What I Learned

AI systems are mirrors. They reflect the quality of their training data. When they fail, do not blame the architecture. Blame the data. And then improve it.

Also: batch complex changes. One massive PR would have been unreviewable. Three focused PRs made each change tractable and kept the conversation productive.


Chapter Six: The Number That Was Too Small

The Discovery

In OpenMythos, a research tool for linear systems, I found a numerical bug.

The expression exp(log_dt + log_A) could produce values smaller than float32 machine epsilon. The outer exponential would round to exactly 1.0. This broke a mathematical guarantee about system stability.

The Investigation

This was outside my comfort zone. I am a software engineer, not a numerical analyst. But I had Google AI to help me understand the floating point semantics.

The AI explained that float32 has limited precision. When you go below that precision, rounding occurs. Sometimes the rounding is harmless. Sometimes, as in this case, it breaks mathematical guarantees.

The Fix

Fix float32 underflow in LTIInjection.get_A() breaking ρ(A) < 1 guarantee #1

After sufficiently large gradient steps, log_dt + log_A can be driven below -20, causing exp(-20) β‰ˆ 2.06e-9 β€” smaller than float32 machine epsilon (β‰ˆ 1.19e-7) β€” so the outer exp(-2.06e-9) rounds to exactly 1.0, silently invalidating the spectral radius stability guarantee.

Change

  • LTIInjection.get_A(): tighten inner clamp lower bound from -20 β†’ -14

At -14: exp(-14) β‰ˆ 8.3e-7, which sits above the float32 ULP threshold at 1.0 (~5.96e-8), ensuring exp(-exp(x)) is always representable as strictly less than 1.0 in float32.

# Before β€” exp(-20) β‰ˆ 2.06e-9 < float32_eps, outer exp rounds to 1.0
return torch.exp(-torch.exp((self.log_dt + self.log_A).clamp(-20, 20)))

# After β€” exp(-14) β‰ˆ 8.3e-7 > ULP threshold, A < 1.0 holds in float32
return torch.exp(-torch.exp((self.log_dt + self.log_A).clamp(-14, 20)))
Enter fullscreen mode Exit fullscreen mode

The upper bound (20) is unchanged; it guards against the opposite extreme (overflow β†’ A β‰ˆ 0), which is not problematic for stability.

I implemented clamping to prevent the intermediate result from falling below the representable range. The fix was validated through both mathematical analysis and empirical testing.

What I Learned

You do not need to be an expert in everything. You need to be willing to learn, to ask questions, and to use the tools available to you. Google AI was my numerical analysis tutor for this fix.

Also: floating point is hard. Every numerical computation is a potential bug. Respect the math, even when you are just writing what looks like simple arithmetic.


The Transformation

When I look back at my journey, I see a clear transformation.

I used to be afraid of big codebases. Now I navigate them with confidence, using AI to understand structure and find my way around.

I used to avoid platform-specific bugs. Now I seek them out, knowing that cross-platform compatibility is where real impact lives.

I used to think cleanup code was an afterthought. Now I know it deserves the same scrutiny as the main logic.

I used to accept assumptions without question. Now I challenge them, especially the old ones, especially the widely accepted ones.

I used to work alone. Now I am part of a global community of maintainers, reviewers, and contributors who make each other better.


By the Numbers

Here is what 373 merged pull requests looks like in practice:

Category Count Description
Security fixes 2 Vulnerability patches preventing exploitation
Platform compatibility 5 Windows-specific and cross-platform fixes
Race conditions 1 Concurrency bug resolution
AI/ML logic 4 Training data and classification improvements
Test infrastructure 4 Test coverage restoration and build fixes
UI/UX fixes 1 User interface state handling
Numerical stability 1 Floating point computation correction
Documentation 1 Build system standardization
Permission handling 1 None-safety in agent communication

The Tools That Powered the Journey

I want to acknowledge the tools because they were essential.

Antigravity Agentic IDE

This AI-native development environment understands context across entire codebases. It helped me navigate unfamiliar projects, trace execution paths, and draft fixes that followed each project's conventions.

Google AI

The AI models behind Antigravity provided:

  • Security vulnerability detection and analysis
  • Cross-platform compatibility research
  • Numerical stability analysis
  • Test gap identification
  • Code review assistance

These tools did not replace my judgment. They amplified my capability. Every fix was reviewed, validated, and approved by human maintainers.


What I Would Tell My Past Self

If I could send a message back to that terrified developer about to submit his first pull request, here is what I would say:

  • You are good enough. Your code is not perfect, but it does not need to be. It needs to be helpful.

  • The community wants you. Maintainers are not waiting to criticize. They are waiting to collaborate.

  • Ask questions. No one knows everything. The best developers are the ones who ask the most questions.

  • Use the tools. AI is not cheating. It is a force multiplier. Use it to understand more, fix more, and learn more.

  • Keep going. The first contribution is the hardest. The hundredth is easier. The three hundred and seventy-third is just another day.


Looking Forward

This challenge has been a milestone, not a destination. There are more bugs to find, more code to improve, more communities to contribute to.

I am grateful to DEV, Sentry, and Google AI for creating this challenge and supporting the open source ecosystem. I am grateful to every maintainer who reviewed my PRs, every user who reported the bugs I fixed, and every developer who inspired me to keep contributing.

I am Aniruddha Adak, and I am just getting started.


Thank you for reading. Connect with me on LinkedIn, follow me on X, or explore my code on GitHub.


Top comments (0)