Skip to content

feat: add candidateScoreNonWinnerQMGMRatio to cast explanation (78787878)#749

Merged
chitcommit merged 1 commit into
mainfrom
auto/78787878-cast-explain-non-winner-qm-gm-ratio
Jun 16, 2026
Merged

feat: add candidateScoreNonWinnerQMGMRatio to cast explanation (78787878)#749
chitcommit merged 1 commit into
mainfrom
auto/78787878-cast-explain-non-winner-qm-gm-ratio

Conversation

@chitcommit

@chitcommit chitcommit commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Adds candidateScoreNonWinnerQMGMRatio to the explain:true output of ch1tty/cast.

Field: candidateScoreNonWinnerQuadraticMean / candidateScoreNonWinnerGeometricMean

  • Present when >= 3 candidates and all non-winner scores > 0
  • Always >= 1 (QM >= GM by Power Mean chain)
  • For n=3: sqrt((r²+t²)/(2·r·t))
  • Identity: ratio * GM === QM
  • Product decomposition: QM/GM === QMToAMRatio * AMGMRatio
  • Completes the full set of pairwise Power Mean ratios (HM, GM, AM, QM) for the non-winner pool

Tests (8/8): presence, bounds (>= 1), identity, n=3 formula, no_match absence, sub-3 absence, product decomposition, description check.


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced cast explanation output with additional statistical metrics for analyzing non-winner candidate score distributions.
  • Tests

    • Added comprehensive test suite validating the new metrics, including edge cases, formula accuracy, and field presence conditions.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds candidateScoreNonWinnerQMGMRatio (quadratic-mean-to-geometric-mean ratio for non-winner candidates) to the ch1tty/cast tool's explanation output. The change updates the schema description string and the buildCastExplanation computed-metrics return block in src/aggregator.ts, and introduces a 262-line test suite validating presence conditions, numeric invariants, identity relationships, edge cases, and the tool-description contract.

Changes

Non-winner QM/GM ratio in cast explanation

Layer / File(s) Summary
Schema description and buildCastExplanation implementation
src/aggregator.ts
Schema description string for ch1tty/cast gains documentation for candidateScoreNonWinnerQMGMRatio. The buildCastExplanation computed-metrics return block is expanded to emit the new ratio alongside existing non-winner statistical fields.
Test suite: presence, invariants, edge cases, and description contract
test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts
New test file with mock backend/aggregator setup and 8 assertions: presence guard (candidateCount ≥ 3 and all non-winner scores > 0), finiteness and ≥ 1 invariant, identity with quadratic/geometric means, n=3 closed-form formula check, absence on no_match, absence with < 3 candidates, product decomposition QMToAMRatio * AMGMRatio == QMGMRatio, and tool-description contract.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • chittyos/ch1tty#465: Modifies buildCastExplanation and the ch1tty/cast explanation schema to add new explanation fields, directly parallel to this PR's addition of candidateScoreNonWinnerQMGMRatio.
  • chittyos/ch1tty#539: Extends the same buildCastExplanation return block and cast explanation schema with another conditionally-included non-winner statistic (candidateScoreSkewness), using the same pattern.
  • chittyos/ch1tty#486: Extends the ch1tty/cast explanation contract and buildCastExplanation() with additional explanation.* fields, following the same schema-plus-implementation structure.

Poem

🐇 Hop, hop, the ratios grow,
QM and GM in a tidy row,
Non-winners measured, sqrt and all,
No fewer than three or the field won't call.
The schema string blooms, the tests all pass —
A ratio of means, as clear as glass! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a new metric (candidateScoreNonWinnerQMGMRatio) to the cast explanation output, fully aligned with the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auto/78787878-cast-explain-non-winner-qm-gm-ratio

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts`:
- Around line 100-104: The assertions in the test file are guarded by
conditional checks that allow tests to pass without validating the contract when
preconditions are not met. Remove the if guards from the assertion blocks to
ensure they always execute and catch regressions. This applies at file
test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts at lines 100-104
(around the candidateScoreNonWinnerQMGMRatio assertions), lines 116-125,
137-145, 157-170, 211-216, and 228-236. Either restructure the test to guarantee
preconditions are always satisfied so guards are unnecessary, or remove the if
conditions entirely and let the assertions run unconditionally to catch when
fields are missing or preconditions drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fb4179b-ab53-4fd2-ac98-7fdc234174ed

📥 Commits

Reviewing files that changed from the base of the PR and between e65e73a and d33d616.

📒 Files selected for processing (2)
  • src/aggregator.ts
  • test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts

Comment on lines +100 to +104
if (explanation.candidateCount >= 3 && explanation.lowestCandidateScore > 0) {
assert.ok('candidateScoreNonWinnerQMGMRatio' in explanation,
`candidateScoreNonWinnerQMGMRatio should be present; keys: ${Object.keys(explanation).join(', ')}`);
assert.equal(typeof explanation.candidateScoreNonWinnerQMGMRatio, 'number', 'should be a number');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Vacuous if guards let core regression checks pass without validating the contract.

Several assertions are conditional, so tests can pass even if the new field is never emitted (or if candidate-count assumptions drift). For this PR objective, these should fail loudly when the expected preconditions are not met.

Suggested tightening (example pattern)
@@
-    if (explanation.candidateCount >= 3 && explanation.lowestCandidateScore > 0) {
-      assert.ok('candidateScoreNonWinnerQMGMRatio' in explanation,
-        `candidateScoreNonWinnerQMGMRatio should be present; keys: ${Object.keys(explanation).join(', ')}`);
-      assert.equal(typeof explanation.candidateScoreNonWinnerQMGMRatio, 'number', 'should be a number');
-    }
+    assert.ok(explanation.candidateCount >= 3, `expected >=3 candidates, got ${explanation.candidateCount}`);
+    assert.ok(explanation.lowestCandidateScore > 0, `expected all candidate scores > 0, got lowest=${explanation.lowestCandidateScore}`);
+    assert.ok('candidateScoreNonWinnerQMGMRatio' in explanation,
+      `candidateScoreNonWinnerQMGMRatio should be present; keys: ${Object.keys(explanation).join(', ')}`);
+    assert.equal(typeof explanation.candidateScoreNonWinnerQMGMRatio, 'number', 'should be a number');
@@
-    if ('candidateScoreNonWinnerQMGMRatio' in explanation) {
+    assert.ok('candidateScoreNonWinnerQMGMRatio' in explanation, 'ratio should be present for this fixture');
+    {
@@
-    if (explanation.candidateCount < 3) {
-      assert.ok(
-        !('candidateScoreNonWinnerQMGMRatio' in explanation),
-        `should be absent with < 3 candidates`,
-      );
-    }
+    assert.ok(explanation.candidateCount < 3, `expected <3 candidates, got ${explanation.candidateCount}`);
+    assert.ok(
+      !('candidateScoreNonWinnerQMGMRatio' in explanation),
+      `should be absent with < 3 candidates`,
+    );

Also applies to: 116-125, 137-145, 157-170, 211-216, 228-236

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts` around lines 100 -
104, The assertions in the test file are guarded by conditional checks that
allow tests to pass without validating the contract when preconditions are not
met. Remove the if guards from the assertion blocks to ensure they always
execute and catch regressions. This applies at file
test/78787878-cast-explain-non-winner-qm-gm-ratio.test.ts at lines 100-104
(around the candidateScoreNonWinnerQMGMRatio assertions), lines 116-125,
137-145, 157-170, 211-216, and 228-236. Either restructure the test to guarantee
preconditions are always satisfied so guards are unnecessary, or remove the if
conditions entirely and let the assertions run unconditionally to catch when
fields are missing or preconditions drift.

@chitcommit chitcommit merged commit 3dd3d06 into main Jun 16, 2026
4 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.

2 participants