Skip to content

fix(TM59): guard TM59CorridorExtendedResult against empty / index-less operativeTemperatures#9

Merged
michaldengusiak merged 1 commit into
sow/2026-Q2from
claude/tm59-corridor-defensive-checks
May 25, 2026
Merged

fix(TM59): guard TM59CorridorExtendedResult against empty / index-less operativeTemperatures#9
michaldengusiak merged 1 commit into
sow/2026-Q2from
claude/tm59-corridor-defensive-checks

Conversation

@michaldengusiak
Copy link
Copy Markdown

@michaldengusiak michaldengusiak commented May 25, 2026

Summary

Defensive guards in TM59CorridorExtendedResult so empty operative-temperature result rows don't crash overheating analysis.

MaxExceedableHours and GetHoursNumberExceeding28 both call OperativeTemperatures.GetMaxIndex().Value / GetMinIndex().Value. When the underlying IndexedDoubles is empty (or sparse enough that no max/min index exists) those .Value accesses on a null Nullable<int> throw InvalidOperationException. Surfaced while running the overheating workflow used to QA the SAM_Tas Stage 2 perf branch.

Adds two guards mirroring the existing operativeTemperatures == null early-return:

  • Count <= 0 → return 0
  • GetMaxIndex() / GetMinIndex() is null → return 0

MaxExceedableHours now uses the locals from the pattern-match (int count = maxIndex - minIndex + 1;) instead of re-calling the API — drops two redundant interop getter calls on the hot path.

GetHoursNumberExceeding28 mirrors the same guard shape for consistency even though the body itself only iterates — keeps the two methods aligned so a future edit doesn't reintroduce the bug.

Behaviour

Input Before After
OperativeTemperatures == null returns -1 returns -1 (unchanged)
Empty OperativeTemperatures throws InvalidOperationException returns 0
No max/min index throws InvalidOperationException returns 0
Well-formed input works works (unchanged)

0 matches the existing convention in this class for "no useful answer" cases.

Companion to SAM_Tas PR

This PR is intended to ship in the same QA round as SAM-BIM/SAM_Tas#10 (Stage 2 perf pass). They are independent code-wise — the SAM_Tas PR doesn't touch TM59 — but this guard fix was surfaced during overheating-workflow QA on that branch.

Per the CI dep-clone convention (workspace-wide changes: push SAM PR first so downstream CIs find the matching sow/** head ref), this PR opens first.

Branch base: sow/2026-Q2.

Test plan

  • CI: build + SPDX.
  • Local: overheating workflow against a model that previously crashed on empty corridor rows — confirm 0 returned, no exception.
  • Spot-check Criterion1 (GetHoursNumberExceeding28() < MaxExceedableHours) — 0 < 0 is false, so empty inputs no longer cause Criterion1 to misreport via the exception path.

Generated by Claude Code

…operativeTemperatures

`MaxExceedableHours` and `GetHoursNumberExceeding28` both consume the
`OperativeTemperatures` IndexedDoubles via `GetMaxIndex().Value` /
`GetMinIndex().Value`. When the underlying collection is empty (or sparse
enough that no max/min index can be derived) those `.Value` accesses on a
null `Nullable<int>` throw `InvalidOperationException`, which surfaces
during overheating analysis whenever a corridor result row comes back with
no operative-temperature samples.

Add defensive guards mirroring the existing `operativeTemperatures == null`
early-return:

  * `operativeTemperatures.Count <= 0`            → return 0
  * `GetMaxIndex()` or `GetMinIndex()` is null    → return 0

`MaxExceedableHours` now uses the locals from the pattern match directly
(`int count = maxIndex - minIndex + 1;`) instead of re-calling the API,
which also drops two redundant getter invocations on the hot path.

`GetHoursNumberExceeding28` matches the same guard shape for consistency
even though the body itself only iterates and doesn't dereference the
indices — keeps the two methods aligned and prevents a future edit from
introducing a regression.

No behavioural change for callers passing well-formed results; previously
crashing inputs now return 0 (matching the existing -1/null-return
convention for "no useful answer" in this class).

Companion to SAM_Tas PR #10 (Stage 2 perf pass) — surfaced while running
the same overheating workflow used to QA that branch. The two PRs are
independent code-wise but intended to ship in the same QA round.

Local Framework MSBuild Release on SAM.Analytical: 0 errors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@michaldengusiak michaldengusiak merged commit 39a5e7e into sow/2026-Q2 May 25, 2026
2 checks passed
@michaldengusiak michaldengusiak deleted the claude/tm59-corridor-defensive-checks branch May 25, 2026 12:54
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