Skip to content

fix: merit NaN guard + hygiene batch#45

Merged
corriander merged 6 commits into
mainfrom
fix/merit-nan-and-hygiene
Jul 13, 2026
Merged

fix: merit NaN guard + hygiene batch#45
corriander merged 6 commits into
mainfrom
fix/merit-nan-and-hygiene

Conversation

@corriander

@corriander corriander commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

A "fixes & hygiene" batch, split into six logical commits.

Task 1 — merit NaN guard

CODA.satisfaction divided total merit by total correlation per requirement. A requirement whose matrix row is entirely CODANull has zero total correlation, so the division produced a silent nan that poisoned .merit. Now such requirements are detected and a clear ValueError is raised naming each offender (e.g. Requirements have no relationships defined: 'Stiffness', 'Weight'), so the scorer fails loud instead of silently. The FIXME is gone. The coda CLI command catches this and reports it via the rich console with a non-zero exit code. Tests added for single/multiple unlinked requirements; the fully-linked case-study values are unchanged.

Task 2 — hygiene

  • docs: from_google_sheetsfrom_google_sheet (real method) in the gsheets guide; phantom BinWMGSheet → real GSheetBinWM in the google sheets API doc.
  • build: dependency typer[all]typer (same version constraint) — the all extra no longer exists upstream and uv sync warned. uv.lock was deliberately regenerated with one explicit uv lock; the diff is minimal (just the extra removed from vdd metadata, no dependency churn).
  • refactor(common): removed the vestigial py2/3 ABC = abc.ABCMeta(...) shim (vdd/common/abstract.py) in favour of stdlib abc.ABC; project requires >=3.11. Usages in vdd/common/__init__.py and vdd/common/io.py updated.
  • fix(coda): compare: CODA.compare(other) returned an elementwise ndarray, not a bool. It now returns True iff shapes match and all corresponding relationships compare equal, reusing CODARelationship.__eq__ (guarded by a shape check first). Docstring and tests added.
  • fix(coda): relationship equality (review follow-up): CODARelationship.__eq__ compared only (correlation, target), so CODAMaximise(0.9, 5) == CODAMinimise(0.9, 5) was True and a zero-correlation relationship compared equal to CODANull — meaning compare could report two models equivalent with a relationship's direction flipped. Equality now requires the concrete types to match, and returns NotImplemented for non-relationship operands instead of raising AttributeError. CODAOptimise's tolerance extension is preserved on top.

Testing

uv run --frozen pytest165 passed (154 baseline + 11 new tests). CLI happy and error paths verified end-to-end.

Judgement calls

  • compare semantics: compare relies on CODARelationship.__eq__ rather than re-deriving type tuples; per review, the equality contract itself was fixed to be type-aware (see above) so the reuse is now sound. A shape guard avoids ndarray broadcasting surprises.
  • CLI error handling: cheap to add, so the coda command now wraps merit/satisfaction access and surfaces the ValueError as a red console message + typer.Exit(code=1) rather than a traceback.

🤖 Generated with Claude Code

corriander and others added 6 commits July 13, 2026 20:40
A requirement whose matrix row is entirely CODANull has zero total
correlation, so satisfaction divided by zero and produced a silent
nan that poisoned merit. Detect these requirements and raise a clear
ValueError naming each offender instead. The CLI coda command catches
this and reports it via the rich console with a non-zero exit code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CODA.compare previously returned the elementwise ndarray from
`self.matrix == other.matrix` rather than a scalar boolean. Return a
proper bool: True iff the models share the same shape and every
corresponding relationship compares equal (CODARelationship already
implements a meaningful __eq__). Add a docstring and tests covering
equal models, differing correlation, and differing shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix the method name `from_google_sheets` -> `from_google_sheet`
(singular) in the gsheets integration guide, and the phantom class
reference `BinWMGSheet` -> `GSheetBinWM` in the google sheets API doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The abstract module provided an ABC base compatible with both Python 2
and 3 via metaclass gymnastics. The project requires >=3.11, so use
stdlib abc.ABC directly and delete the shim module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The `all` extra no longer exists upstream and uv sync warns about it.
Depend on plain `typer` (same version constraint); the required
extras (rich, shellingham) are already pulled in. uv.lock regenerated
via an explicit `uv lock` to stay consistent with pyproject.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CODARelationship.__eq__ compared only correlation and target, so a
maximising relationship compared equal to a minimising one with the
same values, and a zero-correlation relationship compared equal to
CODANull -- meaning CODA.compare could report two models equivalent
when a relationship's direction was flipped. Require the concrete
types to match, and return NotImplemented for non-relationship
operands instead of raising AttributeError. CODAOptimise's tolerance
extension is preserved on top of the base contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@corriander
corriander marked this pull request as ready for review July 13, 2026 19:52
@corriander
corriander merged commit 23dee42 into main Jul 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant