Shared memory and context tools for agentic work.
Code Rooms
{
"schema": "m1nd-bug-hunt-audit-result-v0",
"round_id": "bughunt-humanize-20260514T021500Z",
"lane_id": "audit-09",
"repo_path": "/Users/kle1nz/m1nd/.m1nd-field-workspaces/bughunt-humanize-20260514T021500Z/audit-09/humanize",
"findings": [
"title": "Negative ordinals get the wrong suffix outside teen endings",
"severity": "medium",
"file": "src/humanize/number.py",
"symbol": "ordinal",
"cause": "After converting to int, ordinal chooses the suffix with value % 100 and value % 10 on the signed number. Python modulo maps -1 % 10 to 9, -2 % 10 to 8, and so on, so negative values fall through to the 'th' suffix instead of using the absolute last digit.",
"impact": "Negative ranks or offsets are rendered as '-1th', '-2th', '-21th', etc. The docstring says ordinal works for any integer, and tests only cover positive ordinals plus non-finite values.",
"evidence": [
"src/humanize/number.py:74-104 converts to int and then selects suffix from signed value modulo.",
"tests/test_number.py:14-38 covers positive ordinals but no negative integer cases.",
"Focused run printed: -1 -1th, -2 -2th, -3 -3th, -21 -21th, -101 -101th."
],
"reproduction_or_test": "uv run python - <<'PY'\nimport humanize\nassert humanize.ordinal(-1) == '-1st'\nassert humanize.ordinal(-22) == '-22nd'\nPY",
"confidence": "high"
},
"title": "intcomma never inserts group separators for negative numbers",
"symbol": "intcomma",
"cause": "The formatting loop runs re.sub with the pattern ^(\\d+)(\\d{3}), which only matches strings starting with a digit. Negative values are converted to strings such as '-1234567.89', so the loop exits without adding any separators.",
"impact": "Negative integer, float, and localized numeric strings are returned ungrouped, weakening readability for common money/count values and producing asymmetric output compared with positive inputs.",
"src/humanize/number.py:164-174 builds orig and applies a regex anchored to digits only.",
"tests/test_number.py:41-83 covers positive ints/floats/strings and non-finite values, but no negative finite inputs.",
"Focused run printed intcomma(-1000) => '-1000' and intcomma('-1234567.89') => '-1234567.89'."
"reproduction_or_test": "uv run python - <<'PY'\nimport humanize\nassert humanize.intcomma(-1000) == '-1,000'\nassert humanize.intcomma('-1234567.89') == '-1,234,567.89'\nPY",
"title": "fractional loses or duplicates the sign for negative fractions",
"symbol": "fractional",
"cause": "fractional uses int(number), which truncates negative mixed numbers toward zero, then keeps a negative numerator. For values between -1 and 0, the whole-number branch is skipped and abs(numerator) drops the sign entirely.",
"impact": "Negative fractional values are not data-preserving: -0.3 becomes '3/10' and -1.3 becomes '-1 -3/10'. Any caller displaying debts, offsets, measurements, or deltas can silently show the wrong sign.",
"src/humanize/number.py:370-382 computes whole_number with int(number), uses abs(numerator) for no whole part, and emits the raw signed numerator for mixed numbers.",
"tests/test_number.py:169-189 covers positive fractional values and non-finite values, but no negative fractional values.",
"Focused run printed fractional(-0.3) => '3/10' and fractional(-1.3) => '-1 -3/10'."
"reproduction_or_test": "uv run python - <<'PY'\nimport humanize\nassert humanize.fractional(-0.3) == '-3/10'\nassert humanize.fractional(-1.3) == '-1 3/10'\nPY",
"title": "Integer humanizers crash or misclassify very large integers as infinity",
"symbol": "ordinal/intcomma/intword/apnumber/fractional/scientific/metric",
"cause": "Several number helpers call float(value) as a finite check before or instead of integer formatting, and their except blocks generally catch TypeError and ValueError but not OverflowError. Huge int objects raise OverflowError during float conversion, while huge decimal strings can be converted to float('inf') and returned as '+Inf'.",
"impact": "The package loses Python's arbitrary-precision integer behavior. This directly contradicts ordinal's 'any integer' contract and can crash otherwise valid int inputs or collapse a real integer string into '+Inf'.",
"src/humanize/number.py:74-77, 149-160, 233-236, 300-303, 363-367, 432-435, and 569-570 all rely on float/math finite conversion before formatting.",
"Focused run with n = 10**400 printed OverflowError for ordinal, intcomma, intword, apnumber, fractional, scientific, and metric.",
"Focused run with s = '1' + '0' * 400 printed '+Inf' from ordinal, intcomma, intword, and apnumber."
"reproduction_or_test": "uv run python - <<'PY'\nimport humanize\nn = 10 ** 400\nassert humanize.ordinal(n).endswith('th')\nassert humanize.intcomma(n).startswith('1,000')\ns = '1' + '0' * 400\nassert humanize.ordinal(s) != '+Inf'\nPY",
"confidence": "medium"
"title": "Time humanizers raise OverflowError for infinite numeric seconds",
"file": "src/humanize/time.py",
"symbol": "naturaldelta/naturaltime/precisedelta",
"cause": "naturaldelta calls int(value) and _date_and_delta calls round(value) or datetime.timedelta(seconds=value), but the conversion error handlers do not catch OverflowError for math.inf or -math.inf.",
"impact": "Public APIs that usually return strings for unsupported values can crash on non-finite numeric input. This is an input-hardening gap for callers passing results of calculations, and naturaldelta's return docs explicitly say invalid values are returned unchanged.",
"src/humanize/time.py:86-91 catches only ValueError and TypeError in _date_and_delta.",
"src/humanize/time.py:148-153 catches only ValueError and TypeError around int(value), float(value), and timedelta construction.",
"Focused run printed OverflowError for naturaldelta(inf), naturaldelta(-inf), naturaltime(inf), naturaltime(-inf), precisedelta(inf), and precisedelta(-inf)."
"reproduction_or_test": "uv run python - <<'PY'\nimport math, humanize\nassert humanize.naturaldelta(math.inf) == 'inf'\nassert humanize.naturaltime(math.inf) == 'inf'\nassert humanize.precisedelta(math.inf) == 'inf'\nPY",
"title": "naturalsize crashes on NaN and formats infinities as the largest suffix",
"severity": "low",
"file": "src/humanize/filesize.py",
"symbol": "naturalsize",
"cause": "naturalsize converts input with float(value), but never checks math.isfinite. NaN reaches int(min(log(...))) and raises ValueError; infinities are capped to the last suffix and formatted as 'inf QB' or '-inf QB'.",
"impact": "Non-finite byte counts produce either an exception or a misleading finite-unit label. This is inconsistent with the number helpers' explicit NaN/+Inf/-Inf handling and leaves a missing boundary test in filesize coverage.",
"src/humanize/filesize.py:89-101 computes abs_bytes, log(abs_bytes, base), and suffix index without any finite guard.",
"tests/test_filesize.py:12-96 covers normal finite sizes and negative finite values but no NaN or infinity cases.",
"Focused run printed naturalsize(nan) RAISED ValueError, naturalsize(inf) => 'inf QB', naturalsize(-inf) => '-inf QB'."
"reproduction_or_test": "uv run python - <<'PY'\nimport math, humanize\nassert humanize.naturalsize(math.nan) == 'NaN'\nassert humanize.naturalsize(math.inf) == '+Inf'\nassert humanize.naturalsize(-math.inf) == '-Inf'\nPY",
"title": "activate ignores path changes when reusing the same locale",
"file": "src/humanize/i18n.py",
"symbol": "activate",
"cause": "The translation cache is keyed only by locale. Once a locale has been loaded, later activate(locale, path=...) calls for the same locale return the cached translation without considering the new path.",
"impact": "Applications that switch between packaged translations and custom translation directories can silently keep stale strings from the first path. README documents custom translation paths, but tests do not cover switching the same locale across paths.",
"src/humanize/i18n.py:91-95 checks only 'if locale not in _TRANSLATIONS' and stores _TRANSLATIONS[locale].",
"README.md:223-227 documents activate('pt_BR', path='path/to/my/own/translation/').",
"tests/test_i18n.py:272-326 covers missing default path, English, and None activation, but not same-locale custom path replacement.",
"Mocked focused run printed activate custom path labels => /tmp/path-a /tmp/path-a with only one gettext.translation call."
"reproduction_or_test": "uv run python - <<'PY'\nfrom unittest import mock\nfrom humanize import i18n\nclass T:\n def __init__(self, label): self.label = label\n def gettext(self, msg): return msg\n def ngettext(self, s, p, n): return s if n == 1 else p\n def pgettext(self, c, m): return m\ncalls = []\ndef fake(domain, localedir, languages):\n calls.append(str(localedir))\n return T(str(localedir))\nwith mock.patch.object(i18n.gettext_module, 'translation', side_effect=fake):\n assert i18n.activate('zz_AA', path='/tmp/path-a').label == '/tmp/path-a'\n assert i18n.activate('zz_AA', path='/tmp/path-b').label == '/tmp/path-b'\nPY",
}
"commands_run": [
"sed -n '1,260p' /Users/kle1nz/m1nd/docs/benchmarks/bug-hunt-rounds/bughunt-humanize-20260514T021500Z/lane-prompts/audit-09.md",
"rg --files -g '!*.pyc' -g '!__pycache__'",
"git status --short",
"sed/nl reads of pyproject.toml, README.md, docs/*.md, src/humanize/*.py, and tests/*.py",
"rg -n \"TODO|FIXME|xfail|skip|ValueError|OverflowError|round\\(|tzinfo|decimal_separator|thousands_separator|float\\(|int\\(|len\\(|IndexError|KeyError|Unit\\[|format %|%0|math\\.log10|log\\(\" src tests docs",
"uv run --extra tests pytest -q",
"uv run python - <<'PY' ... boundary probes for intcomma, fractional, time non-finite values, filesize non-finite values, i18n custom path caching ... PY",
"uv run python - <<'PY' ... negative ordinal probe ... PY",
"uv run python - <<'PY' ... huge integer/string probe ... PY"
"files_inspected": [
"pyproject.toml",
"README.md",
"docs/number.md",
"docs/time.md",
"docs/filesize.md",
"docs/i18n.md",
"src/humanize/__init__.py",
"src/humanize/number.py",
"src/humanize/time.py",
"src/humanize/filesize.py",
"src/humanize/i18n.py",
"src/humanize/lists.py",
"tests/test_number.py",
"tests/test_time.py",
"tests/test_filesize.py",
"tests/test_i18n.py",
"tests/test_lists.py"
"tests_run": [
"uv run --extra tests pytest -q -> 685 passed, 69 skipped in 0.63s",
"One-off uv run python boundary probe -> reproduced intcomma negative grouping gap, fractional negative sign loss, time infinity OverflowError, naturalsize NaN/Inf behavior, and i18n same-locale path cache reuse.",
"One-off uv run python negative ordinal probe -> reproduced '-1th', '-2th', '-21th', '-101th'.",
"One-off uv run python huge integer/string probe -> reproduced OverflowError for 10**400 int inputs and '+Inf' for a 401-digit integer string."
"m1nd_usage": [
"No m1nd MCP tools, m1nd CLI helpers, probe_m1nd.py, graph retrieval, or m1nd repo docs were used for the audit. The lane prompt explicitly prohibited them."
"final_notes": "No source patches, commits, pushes, or publications were made. The repo worktree remained clean after the audit. I did not read operator-only files. Findings are based on local source/test inspection plus focused reproductions; I did not inspect locale .po correctness beyond the cache behavior around custom paths.",
"agent_testimony": "I treated this as a fresh correctness/regression audit. The normal test suite is healthy, so I concentrated on boundary classes missing from tests: negative finite numbers, non-finite numeric seconds and byte counts, huge Python integers, and custom translation path switching. The strongest issues are the negative number formatting regressions and the i18n path cache because they affect ordinary-looking inputs and are easy to cover with focused tests.",