fix: merit NaN guard + hygiene batch#45
Merged
Merged
Conversation
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
marked this pull request as ready for review
July 13, 2026 19:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A "fixes & hygiene" batch, split into six logical commits.
Task 1 — merit NaN guard
CODA.satisfactiondivided total merit by total correlation per requirement. A requirement whose matrix row is entirelyCODANullhas zero total correlation, so the division produced a silentnanthat poisoned.merit. Now such requirements are detected and a clearValueErroris 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. ThecodaCLI 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
from_google_sheets→from_google_sheet(real method) in the gsheets guide; phantomBinWMGSheet→ realGSheetBinWMin the google sheets API doc.typer[all]→typer(same version constraint) — theallextra no longer exists upstream anduv syncwarned.uv.lockwas deliberately regenerated with one explicituv lock; the diff is minimal (just the extra removed from vdd metadata, no dependency churn).ABC = abc.ABCMeta(...)shim (vdd/common/abstract.py) in favour of stdlibabc.ABC; project requires >=3.11. Usages invdd/common/__init__.pyandvdd/common/io.pyupdated.compare:CODA.compare(other)returned an elementwise ndarray, not a bool. It now returnsTrueiff shapes match and all corresponding relationships compare equal, reusingCODARelationship.__eq__(guarded by a shape check first). Docstring and tests added.CODARelationship.__eq__compared only(correlation, target), soCODAMaximise(0.9, 5) == CODAMinimise(0.9, 5)wasTrueand a zero-correlation relationship compared equal toCODANull— meaningcomparecould report two models equivalent with a relationship's direction flipped. Equality now requires the concrete types to match, and returnsNotImplementedfor non-relationship operands instead of raisingAttributeError.CODAOptimise's tolerance extension is preserved on top.Testing
uv run --frozen pytest— 165 passed (154 baseline + 11 new tests). CLI happy and error paths verified end-to-end.Judgement calls
comparerelies onCODARelationship.__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.codacommand now wraps merit/satisfaction access and surfaces theValueErroras a red console message +typer.Exit(code=1)rather than a traceback.🤖 Generated with Claude Code