Skip to content

jc reliability: enforce the no-NaN contract on non-finite input (follow-up to #709)#710

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/review-claude-board-files-nhqgx1
Jul 17, 2026
Merged

jc reliability: enforce the no-NaN contract on non-finite input (follow-up to #709)#710
AdaWorldAPI merged 2 commits into
mainfrom
claude/review-claude-board-files-nhqgx1

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

Follow-up bug fix to #709 (the P5a jc::reliability battery). The module doc promised all four APIs return None "rather than panicking or returning NaN", but non-finite input slipped through the guards.

The bug (two reviewers, independently)

  • CodeRabbit (Major) + Codex both flagged post-merge on P5a: jc reliability/validity metric battery (Pearson / Spearman / Cronbach α / ICC) #709 that the no-NaN contract was not enforced.
  • spearman(&[1.0, f64::NAN, 2.0], &[1.0, 2.0, 3.0]) returned Some(1.0)average_ranks maps NaN (via partial_cmp → Equal) to an ordinary finite rank, so the NaN never reached the delegated pearson guard and produced a finite-but-garbage ρ.
  • pearson / cronbach_alpha / icc returned Some(NaN) for non-finite input.

This matters because the four measures feed the D-TRI agreement gates; a silent Some(NaN) / finite-garbage estimate would poison those gates.

The fix

  • all_finite helper + up-front if !all_finite(..) { return None } guard on all four public APIs. spearman guards the raw values before ranking (not just via pearson), which is the case the guard has to catch.
  • Trailing x.is_finite().then_some(x) result guards so finite-but-overflowing magnitudes (deviations → ±∞ → ratio NaN) also return None.
  • Two regression tests: non_finite_inputs_return_none (NaN/±∞ across all four, incl. the reported reproducer) and overflowing_large_finite_inputs_return_none_not_nan (1e308 magnitudes).
  • Softened the "ten pillars" aside in the module doc to "registered pillars" (the registry holds 12; a hard count in a doc-comment is a drift magnet).

Verification

  • cargo test --manifest-path crates/jc/Cargo.toml --lib reliability → 14 passed.
  • cargo test --doc (reliability) → 3 doctests passed.
  • cargo clippy --lib → no warnings in reliability.rs.

Board hygiene

.claude/board/EPIPHANIES.md prepended with E-JC-BATTERY-NO-NAN-CONTRACT-1 (the merged E-JC-IS-NOT-A-METRIC-BATTERY-1 entry is append-only and left untouched).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Awg6TXocHcwTtc6eGsHcdD


Generated by Claude Code

The battery's module doc promised `None` "rather than returning NaN", but
non-finite input slipped through: spearman(&[1.0, NAN, 2.0], ...) returned
Some(1.0) (average_ranks maps NaN to a finite rank, so it never reached the
delegated pearson guard) and pearson/cronbach_alpha/icc returned Some(NaN).

Add an `all_finite` helper + up-front finite guards on all four public APIs
(spearman guards the raw values before ranking), plus trailing result guards
so finite-but-overflowing magnitudes (deviations -> +/-inf -> ratio NaN) also
return None. Two regression tests cover NaN/+/-inf (incl. the reported
reproducer) and 1e308-overflow inputs.

Also soften the "ten pillars" aside in the module doc to "registered pillars"
(the registry holds 12; a hard count in a doc-comment is a drift magnet).

Found post-merge by CodeRabbit (Major) and Codex independently on #709.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Awg6TXocHcwTtc6eGsHcdD
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5410aa10-7d47-409e-ad5c-ee275e87efc3)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@AdaWorldAPI, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 572b3fb1-96f7-456e-adc9-bbccb647ba71

📥 Commits

Reviewing files that changed from the base of the PR and between 5249feb and 0f0bba1.

📒 Files selected for processing (2)
  • .claude/board/EPIPHANIES.md
  • crates/jc/src/reliability.rs

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.

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review July 17, 2026 13:27

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db4889dc76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let r = sxy / denom;
// Even with finite inputs, large magnitudes can overflow the squared
// deviations to ±∞, making the ratio non-finite — reject that too.
r.is_finite().then_some(r)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return None when Pearson's denominator overflows

For large but finite inputs where sxx and syy are still finite but their product overflows, this final is_finite check does not catch the bad result because denom becomes inf and sxy / denom can be a finite 0.0. For example, pearson(&[1e100, -1e100], &[1e100, -1e100]) is perfectly correlated but returns Some(0.0) rather than None, so the new overflow/no-garbage contract is still violated for finite high-magnitude data.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch — confirmed. sxx*syy overflows to before sqrt, so denom = ∞ and sxy/∞ collapses to a finite-but-wrong 0.0 that the trailing is_finite() guard doesn't reject. Fixed in 0f0bba1 by guarding denom == 0.0 || !denom.is_finite() in pearson (and the same overflowed-denominator pattern in cronbach_alpha's total_var and icc's denom). The overflow test now includes pearson(&[1e100,-1e100],&[1e100,-1e100]) == None.


Generated by Claude Code

The trailing is_finite() guards missed one finite-but-wrong case: when the
squared-deviation product (pearson), subject-total variance (cronbach), or
mean-square sum (icc) overflows a finite input to +inf, the denominator
becomes inf and numerator/inf collapses to a FINITE 0.0. Example flagged by
Codex: pearson(&[1e100,-1e100],&[1e100,-1e100]) is perfectly correlated but
returned Some(0.0) instead of None.

Guard `denom == 0.0 || !denom.is_finite()` in all three ratio computations so
an overflowed denominator returns None. The existing result-finiteness guards
stay for the non-finite-numerator case. Extend the overflow test with the
exact Codex reproducer; note in-test why spearman is immune (it Pearson's the
bounded 1..n ranks, which never overflow).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Awg6TXocHcwTtc6eGsHcdD
@AdaWorldAPI
AdaWorldAPI merged commit abbe15a into main Jul 17, 2026
7 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Jul 17, 2026
…::reliability)

D-TRI-2 (12-step ↔ 12-family agreement) is NO-GO pre-mint: both 12-vectors are
codebook views of the unbuilt minted register (D-TRI-1). Scoped and recorded
(E-D-TRI-2-MINT-BLOCKED-1) so no future session re-attempts it.

The mint-free cousin: measure whether the three SHIPPED 12-family param tables
actually agree — the exact D-TSC-1 "five tables diverged silently" failure mode,
using the jc::reliability battery (#709/#710).

New probe crates/jc/examples/style_table_agreement.rs (transcribes UNIFIED_STYLES,
thinking-engine StyleParams, planner FieldModulation with per-value // SOURCE
provenance; verified against source). Runs ICC(2,1)/ICC(3,1)/Cronbach α/pairwise
Pearson-Spearman per shared dim in all-12 and 7-explicit modes; pre-registered
ICC bands 0.75/0.50; exit-0 (DISTINCT is a valid finding, never a build failure).

Measured verdict:
- UNIFIED_STYLES ≡ thinking-engine StyleParams: PERFECT (r=ρ=1.0) — M9-dedup
  confirmed, the two are one table in two homes.
- Planner FieldModulation's 7 explicitly-calibrated families: IDENTITY (ICC 0.90-0.98).
- ONLY drift: the 5 planner default_modulation fallbacks (convergent/systematic/
  divergent/diffuse/peripheral → flat 0.7/6/0.3) drop all-12 ICC to 0.71 (AMBIGUOUS).
  → TD-PLANNER-STYLE-DEFAULT-DRIFT-1: fill them from the canonical table.

Board: EPIPHANIES E-D-TRI-2-MINT-BLOCKED-1, STATUS_BOARD D-TSC-1b, TECH_DEBT,
triangle plan §6 annotation, AGENT_LOG. Measurement-only, no behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Awg6TXocHcwTtc6eGsHcdD
AdaWorldAPI pushed a commit that referenced this pull request Jul 17, 2026
IMPLEMENT — arigraph/community.rs: TripletGraph::communities() — multi-level
Louvain modularity over the triplet adjacency (NARS-confidence-weighted edges,
self-loops dropped), a carrier method (litmus-compliant), deterministic
(BTreeMap-ordered moves + sorted-entity index), 5 inline tests. The STRUCTURAL
partition complementing the episodic/family (part_of:is_a) basins AriGraph owns.
Louvain core verified standalone before the in-crate build (two-triangle -> 2
comms Q=0.357, clique -> 1, determinism, empty-safe, weighted-cohesion); built
against jc/examples/splat_louvain_modularity.rs (parallel-session reference).
Leiden refinement pass + PPR/community fusion into retrieval.rs are D-GR-3b.

WRITE — plan v1.2:
- The base is a MEANING MEASUREMENT: COCA + NARS are co-equal core faculties;
  agnosticism is scoped to raw data + consumer modelling only (NOT meaning). The
  12-byte facet is 6x2x8bit (CLAM), never f32 (f32 is build-time source only);
  256 = ranking (needs cosine-replacement), 256^2 = distribution (as accurate as
  content-blind f32); the 6x(8:8) register is polymorphically part_of:is_a
  family-identity OR palette256^2 centroid (one register, two lenses) (S3a).
- Leiden community synergies: distributional-meaning modes, NARS-truth-weighted
  (6 axes; the rung-3 layer of the ascent) (S3b).
- DocumentID-KV / witness-handle seam: a stable DocumentID keys the consumer KV;
  the witness/node hold handles only, never raw bytes (S4a, D-GR-6).
- New probes: P-COMMUNITY-BASIN-AGREE, P-HIER-LEIDEN-HHTL,
  P-MEASUREMENT-DETERMINISM, P-PQ-RANK (all via jc::reliability, merged #709/#710).
- Meaning-substrate hazard (S7.6): do not reintroduce "keep language out"; the
  only real constraint is crate-layering (core can't dep the downstream deepnsm
  crate; use the in-core nsm/ copy).

Board hygiene: INTEGRATION_PLANS entry updated to v1.2.

Co-Authored-By: Claude <noreply@anthropic.com>
AdaWorldAPI pushed a commit that referenced this pull request Jul 17, 2026
IMPLEMENT — arigraph/community.rs: TripletGraph::communities() — multi-level
Louvain modularity over the triplet adjacency (NARS-confidence-weighted edges,
self-loops dropped), a carrier method (litmus-compliant), deterministic
(BTreeMap-ordered moves + sorted-entity index), 5 inline tests. The STRUCTURAL
partition complementing the episodic/family (part_of:is_a) basins AriGraph owns.
Louvain core verified standalone before the in-crate build (two-triangle -> 2
comms Q=0.357, clique -> 1, determinism, empty-safe, weighted-cohesion); built
against jc/examples/splat_louvain_modularity.rs (parallel-session reference).
Leiden refinement pass + PPR/community fusion into retrieval.rs are D-GR-3b.

WRITE — plan v1.2:
- The base is a MEANING MEASUREMENT: COCA + NARS are co-equal core faculties;
  agnosticism is scoped to raw data + consumer modelling only (NOT meaning). The
  12-byte facet is 6x2x8bit (CLAM), never f32 (f32 is build-time source only);
  256 = ranking (needs cosine-replacement), 256^2 = distribution (as accurate as
  content-blind f32); the 6x(8:8) register is polymorphically part_of:is_a
  family-identity OR palette256^2 centroid (one register, two lenses) (S3a).
- Leiden community synergies: distributional-meaning modes, NARS-truth-weighted
  (6 axes; the rung-3 layer of the ascent) (S3b).
- DocumentID-KV / witness-handle seam: a stable DocumentID keys the consumer KV;
  the witness/node hold handles only, never raw bytes (S4a, D-GR-6).
- New probes: P-COMMUNITY-BASIN-AGREE, P-HIER-LEIDEN-HHTL,
  P-MEASUREMENT-DETERMINISM, P-PQ-RANK (all via jc::reliability, merged #709/#710).
- Meaning-substrate hazard (S7.6): do not reintroduce "keep language out"; the
  only real constraint is crate-layering (core can't dep the downstream deepnsm
  crate; use the in-core nsm/ copy).

Board hygiene: INTEGRATION_PLANS entry updated to v1.2.

Co-Authored-By: Claude <noreply@anthropic.com>
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