Real projects · real findings

Bugs found. Automatically.

These issues were found by tailtest running alongside a Claude Code session. No test was written manually — tailtest generated the scenarios, ran them, and surfaced what failed.

securo-finance/securo

Personal finance · open source

Python ★ 536

Recurring transactions permanently lose their date after February

Monthly transactions on the 29th, 30th, or 31st get clamped to Feb 28 when February arrives. On the next advance, the function reads the clamped day instead of the original — the intended date is permanently gone.

_advance_date(date(2026, 1, 31), "monthly")
→ 2026-02-28 # correct (Feb has 28 days)
_advance_date(date(2026, 2, 28), "monthly")
→ 2026-03-28 # wrong — should be 2026-03-31
dongdongbh/Mindwtr

GTD productivity · open source

TypeScript ★ 737

Monthly calendar events set by weekday appear on the wrong date

Events set to repeat on "the first Monday of the month" use a fixed day number instead. A meeting every first Tuesday shows up on the 6th of each month — regardless of what day that is.

RRULE:FREQ=MONTHLY;BYDAY=1MO # first Monday of each month
Jan 6 → Feb 3 → Mar 3 → Apr 7 # expected
Jan 6 → Feb 6 → Mar 6 → Apr 6 # actual
theDakshJaitly/mex

Docs drift detector · open source

TypeScript ★ 655

A file that trips both staleness thresholds is penalised twice

When a file exceeds both the day threshold (≥90 days) and the commit threshold (≥200 commits), checkStaleness returns two separate STALE_FILE errors for the same condition. computeScore deducts 10 points per error, so one stale file silently subtracts 20 points instead of 10.

checkStaleness("docs/api.md", cwd) // days=100, commits=250
→ [{ code: "STALE_FILE", message: "100 days..." },
{ code: "STALE_FILE", message: "250 commits..." }]
computeScore([issue1, issue2]) // deducts 10 + 10 = 20
paperclipai/paperclip

AI-native code editor · open source

TypeScript ★ 53749

Monthly backup retention uses a fixed 30-day month, deleting backups prematurely

pruneOldBackups calculates the monthly cutoff as monthlyMonths × 30 days. In a 31-day month, a backup from 2 calendar months ago is 61–62 days old — past the 60-day cutoff — and gets deleted even though it should be kept.

// monthlyMonths: 2 → cutoff = now - 60 days
// Today: March 31. Backup from Jan 28 is 62 days old → deleted.
// Correct: Jan 28 is within 2 calendar months → should be kept.
const monthlyCutoff = now - Math.max(1, retention.monthlyMonths) * 30 * 24 * 60 * 60 * 1000;
rtk-ai/rtk

Code complexity analysis · Mozilla

Rust ★ 26594

Two filter bugs corrupt comment stripping and function-body extraction

MinimalFilter: a single-line """...""" docstring leaves in_docstring=true, so a # comment between two single-line docstrings is incorrectly stripped. AggressiveFilter: lines starting with fn foo() { skip brace counting, causing let bindings after the first body line to leak out. 2 issues filed.

# MinimalFilter — comment suppressed between two single-line docstrings
"""doc A"""
# this comment is incorrectly stripped ← bug
"""doc B"""
expectedparrot/edsl

AI survey framework · open source

Python ★ 454

uniquify() produces duplicate IDs when suffixed names already exist in the list

When a ScenarioList contains a value like "item_1" and uniquify("id") is called on a list that also has duplicate "item" entries, the function generates "item_1" as a suffix — colliding with the pre-existing "item_1". The result has duplicate IDs, breaking the uniqueness contract.

sl = ScenarioList([Scenario({"id": "item"}),
Scenario({"id": "item_1"}), # pre-existing
Scenario({"id": "item"})])
ids = [s["id"] for s in sl.uniquify("id")]
# Expected: ['item', 'item_1', 'item_2']
# Actual: ['item', 'item_1', 'item_1'] ← duplicate
python-cmd2/cmd2

Interactive CLI library

Python ★ 687

Module-level REDIRECTION_TOKENS list grows without bound on every alias or macro command

Four functions in cmd2.py (_alias_create, _alias_list, _macro_create, _macro_list) assign constants.REDIRECTION_TOKENS by reference and call .extend() on it, permanently mutating the module-level list. Each invocation appends terminators; after 5 alias creates the list grows from 3 to 10 entries. Two Cmd() instances in the same process share the corrupted state — instance A's alias operations affect instance B's redirection parsing.

tokens_to_unquote = constants.REDIRECTION_TOKENS # reference, not copy
tokens_to_unquote.extend(self.statement_parser.terminators) # mutates module global
# After 5 alias creates: ['|', '>', '>>', ';', ';', ';', ';', ';', ';', ';', ';']
# Fix: list(constants.REDIRECTION_TOKENS)
View GitHub issue #1649 Fixed by maintainer
mattrobenolt/jinja2-cli

CLI for Jinja2 rendering

Python ★ 598

KeyError leaks from get_format() and has_format() when the format string is unknown

get_format(fmt) only catches ModuleNotFoundError; when fmt is not present in the formats dict (e.g. 'nonexistent'), formats[fmt] raises KeyError which propagates uncaught instead of being wrapped as InvalidDataFormat. has_format() inherits the same root cause: it only catches InvalidDataFormat, so the KeyError leaks through and has_format("unknown") raises instead of returning False. Found via V13 native adversarial mode (depth: adversarial) on 2026-04-25.

# jinja2cli/cli.py:74
def get_format(fmt):
try: return formats[fmt]() # KeyError on unknown fmt
except ModuleNotFoundError: raise InvalidDataFormat(fmt) # leaks KeyError
# Fix: except (KeyError, ModuleNotFoundError):
maldoinc/wireup

Type-driven dependency injection

Python ★ 388

Module discovery crashes on PEP 420 namespace packages and traverses hidden dirs like .git

_find_objects_in_module accesses module.__file__ unconditionally, but PEP 420 namespace packages have no __file__ and crash with AttributeError. Three more robustness issues in the same file: endswith('__init__.py') false-positives on not__init__.py; hidden directories like .git and .mypy_cache are traversed and importlib.import_module is called with invalid dot-prefixed names; one broken submodule kills the entire discovery scan because there is no try/except around the import.

# wireup/_discovery.py:62
module.__file__ # AttributeError on namespace packages
# Fix: getattr(module, "__file__", None)
# 3 more bugs in same file: __init__ false-positive, hidden dirs, broken submodule kills scan
omni-us/jsonargparse

CLIs from type hints

Python ★ 424

Forward-ref resolution crashes when run from __main__ context

resolve_forward_ref calls __builtins__.copy(). In regular Python modules, __builtins__ is a dict; in __main__ and many embedding contexts it is the builtins module — and modules don't have a copy method. Calling jsonargparse from a __main__ script crashes. Four more edge-case crashes in the same file: bare tuple/set types (no __origin__), and Dict[int, str] with non-numeric or empty-string keys (raw ValueError leak from int()).

# _typehints.py:777
__builtins__.copy() # AttributeError when __builtins__ is the module
# Fix: import builtins; vars(builtins).copy()
# 4 more crashes: bare tuple/set origin, Dict[int, str] non-numeric keys
luolingchun/flask-openapi

OpenAPI for Flask

Python ★ 265

Single-line docstring descriptions silently vanish from generated OpenAPI specs

get_operation has a backward condition: lines[0] if len(lines) == 0 else '<br/>'.join(lines[1:]). split() always returns at least one element, so len(lines) == 0 is always False — single-line docstrings always go through the join branch and produce an empty string. The description is silently lost. Two more bugs in parse_method: routes registered with HEAD or OPTIONS are silently dropped because the if/elif ladder only handles GET/POST/PUT/PATCH/DELETE.

# flask_openapi/utils.py:58
doc_description = lines[0] if len(lines) == 0 else "<br/>".join(lines[1:])
# len(lines) == 0 is always False after split() ← dead code
# Fix: len(lines) <= 1
itsDNNS/docsight

Self-hosted DOCSIS evidence system

Python ★ 228

api_restore proceeds with no config_manager and writes to a hardcoded /data path

When get_config_manager() returns None, api_restore does not bail early. It falls through to a fallback that uses '/data' as the data directory and proceeds to extract the user's uploaded archive into a hardcoded /data path on the host filesystem. The sibling api_backup_download endpoint correctly returns 500 in the same condition.

# app/modules/backup/routes.py
data_dir = _config_manager.data_dir if _config_manager else "/data" # falls through
# api_restore proceeds and unpacks to /data ← bug
# api_backup_download returns 500 in the same case (correct)
View GitHub issue #357 Fixed by maintainer
deep5050/radio-active

Terminal radio player

Python ★ 584

Newline in alias name corrupts the alias file with phantom radio entries

add_entry writes name and url directly to the alias file with no sanitization. A name containing a newline (TUI input glitch, clipboard paste, scripted alias creation) splits across two lines and creates a phantom alias on the next read. The search() method also has a state leak: self.found is set to True on a hit but never reset to False on a miss, so any miss following a previous hit reports a stale True.

# radioactive/alias.py:87
f.write(f"{left}=={right}\n") # no sanitization
# add_entry("evil\nstation", "http://url1") writes:
# evil==
# station==http://url1 ← phantom entry

Full sweep

Every repo we've tested

14 bugs confirmed across 34 repos swept (2 already fixed and merged by maintainers within 24 hours of filing). The clean results are signal too -- well-tested code passes.

Repo Result
mattrobenolt/jinja2-cli Bug filed #145
python-cmd2/cmd2 Bug filed #1649
maldoinc/wireup Bug filed #135
omni-us/jsonargparse Bug filed #904
luolingchun/flask-openapi Bug filed #262
itsDNNS/docsight Bug filed #357
deep5050/radio-active Bug filed #150
securo-finance/securo Bug filed #67
dongdongbh/Mindwtr Bug filed #380
theDakshJaitly/mex Bug filed #31
paperclipai/paperclip Bug filed #3713
rtk-ai/rtk Bug filed #1322
expectedparrot/edsl Bug filed #2446
getcompanion-ai/feynman No findings
basicmachines-co/basic-memory No findings
sgcarstrends/backend No findings
berrydev-ai/blockdoc-python No findings
stefanjudis/cchooks No findings
lamoom-ai/lamoom No findings
badlogic/claude-code-tools-python No findings
claudekit-dev/claudekit No findings
rulesync No findings
vibe-log-cli No findings
tdd-guard No findings
cc-flow No findings
claude-hub No findings
tsk No findings
claude-squad No findings
aws-mcp No findings
ccpm Skipped
coffee-analytics Skipped

Skipped = no testable source code (skills-only or SQL-only repos)

Get started

Run it on your own project.

Install in 60 seconds. No test files to write, no configuration, no commands.

Claude Code plugin
$ claude plugin marketplace add avansaber/tailtest
$ claude plugin install tailtest@avansaber-tailtest