Skip to content

chore(deps): Bump NCalcSync from 6.4.0 to 7.1.0 - #96

Merged
tylerkron merged 1 commit into
mainfrom
dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0
Sep 3, 2026
Merged

tylerkron merged 1 commit into
mainfrom
dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 10, 2026

Copy link
Copy Markdown
Contributor

Updated NCalcSync from 6.4.0 to 7.1.0.

Release notes

Sourced from NCalcSync's releases.

7.1.0

What's Changed

NCalc docs re-design

With the help of Hallmark. Check it here: https://ncalc.github.io/ncalc

New Contributors

Full Changelog: ncalc/ncalc@v7.0.2...v7.1.0

7.0.2

What's Changed

Full Changelog: ncalc/ncalc@v7.0.1...v7.0.2

7.0.1

What's Changed

Full Changelog: ncalc/ncalc@v7.0.0...v7.0.1

7.0.0

What's Changed

Breaking Changes

  • ExpressionContext now represents only per-evaluation runtime state. StaticParameters was renamed to Parameters, and Options, CultureInfo, MathHelperOptions, ComparisonOptions, and the implicit conversions from ExpressionOptions/CultureInfo were removed. Configure parsing and evaluation with ExpressionConfiguration instead.
  • ExpressionContext is no longer a record and all props are init only to incentive immutability.
  • Expression.Options no longer exposes the getter. It is kept only as a setter that replaces Expression.Configuration using ExpressionConfiguration.FromOptions(...). Use Expression.Configuration, Expression.ParserOptions, Expression.EvaluationOptions, Expression.CultureInfo, and Expression.Configuration.CacheEnabled.
  • Expression constructors and IExpressionFactory.Create now accept ExpressionConfiguration and CultureInfo.
  • Parameters, DynamicParameters, AsyncParameters, Functions, and AsyncFunctions on Expression no longer have setters. Mutate the dictionaries on Expression.Context or replace them through a new ExpressionContext.
  • LogicalExpressionArgumentSeparator was renamed to ArgumentSeparator.
  • LogicalExpressionParserOptions no longer exposes DecimalAsDefault and LongAsDefault. Use FloatingPointNumberType and IntegerNumberType.
  • LogicalExpressionParser.GetOrCreateExpressionParser now requires a CultureInfo, and LogicalExpressionParser.Parse accepts culture separately from parser options.
  • ILogicalExpressionFactory.Create now accepts LogicalExpressionParserOptions? and CultureInfo? instead of ExpressionOptions.
  • IEvaluationVisitorFactory, EvaluationVisitor, and AsyncEvaluationVisitor now require ExpressionEvaluationOptions and CultureInfo.
  • LogicalExpression extension methods Evaluate(...) and EvaluateAsync(...) were removed. Evaluate through Expression or instantiate the appropriate evaluation visitor.
  • ComparisonOptions was removed. Use StringComparer property.
  • MathHelperOptions was renamed to MathOptions.
  • LogicalExpressionParserContext was renamed to LogicalExpressionParseContext following Parlot convention
  • The NCalc.Antlr package/plugin was removed. Use the default Parlot parser or provide a custom ILogicalExpressionFactory.

Migration Guide: https://ncalc.github.io/ncalc/articles/project/migrate_v6_to_v7.html

Full Changelog: ncalc/ncalc@v6.4.0...v7.0.0

Commits viewable in compare view.

@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Aug 10, 2026
@dependabot
dependabot Bot force-pushed the dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0 branch from 310bbec to 303acd6 Compare August 14, 2026 23:31
tylerkron added a commit that referenced this pull request Aug 31, 2026
check_dependabot_claims.py only looked at .csproj PackageReference pins,
so a transitive-only update — a security bump to something no csproj
names — would have failed the check. That is the most costly PR to block
on a false positive.

Lock files are safe to trust here: in #130 the lock file recorded
Daqifi.Core at 1.3.0 right alongside the csproj, because the bump landed
nowhere. Re-verified against the repo's real Dependabot PRs — still
passes #93/#95/#96, still fails #130 on all ten unapplied claims.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tylerkron added a commit that referenced this pull request Aug 31, 2026
… one

Qodo, correctly: accepting a claim because SOME manifest reached the new
version lets a grouped multi-directory PR move one head and leave another
stale. Avalonia.Fonts.Inter is pinned by three projects here, so that is a
real input shape, not a hypothetical.

Now every project pinning the package must be at the claimed version —
scoped to the `directories` Dependabot actually manages, read from
dependabot.yml at check time. The scope matters in both directions: the
iOS head is not managed, so its pins drift on purpose and must not fail a
PR that could never have touched them; and if the list grows later, the
check grows with it. An unreadable config is exit 2, never a silently
widened scope.

Qodo's suggested fix — carrying per-claim directory context out of the PR
body — does not work: Dependabot dedupes a package across directories, so
a grouped body states each bump once regardless of how many directories
it touches.

Lock files stay evidence-only, never grounds for failure. A legitimate
single-directory PR leaves the other directories' lock files recording
the old transitive version (#93 changed only Daqifi.Avalonia/), so
failing on that would reject good PRs.

Re-verified against the repo's real Dependabot PRs: passes #93/#95/#96,
fails #130 on all ten unapplied claims, and the scope correctly excludes
the iOS head. Self-test grows to 28 cases, including Qodo's exact
scenario.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tylerkron added a commit that referenced this pull request Aug 31, 2026
…pped (#137)

* chore(deps): make Daqifi.Core updates visible instead of silently skipped

Daqifi.Core sat at 1.3.0 for five weeks while four releases shipped, and
nothing said so. The assumption was that Dependabot had never opened a PR
for it. It had — twice — but never in a way anyone could see:

  #93  bumped Core 1.3.0 -> 1.4.0, titled "the minor-and-patch group with
       3 updates"; Core appears nowhere in the title.
  #95  superseded it with 1.3.0 -> 1.5.0, titled after Avalonia.
  08-17 Dependabot auto-closed #95 and opened no replacement. Two weeks of
       silence while 1.6.0 and 1.7.0 shipped.
  #130 announced Core 1.3.0 -> 1.7.0 in its body and never touched the
       csproj. Ten of its fourteen announced updates are missing from the
       diff.

The NU1605 downgrade theory does not explain it: Core 1.4.0/1.5.0/1.6.0
all depend on System.IO.Ports 10.0.10, exactly what was pinned. Only 1.7.0
raised the floor.

Three guards, one per failure:

- Daqifi.Core is excluded from the minor-and-patch group, so it always
  gets its own "Bump Daqifi.Core from X to Y" PR — visible in a PR list,
  and not droppable from a group whose other members moved.
- check_dependabot_claims.py fails a Dependabot PR whose body claims a
  bump its manifests do not show. Verified against this repo's history:
  passes #93/#95/#96, fails #130 on all ten unapplied claims.
- check_core_drift.py compares the pin against nuget.org weekly and files
  a rolling issue, independent of Dependabot having done anything at all.

Both guards ship with self-tests, and build.yml now runs every
.github/scripts self-test on each push and PR — without that, these two
would only be exercised on a Dependabot PR or on a Monday.

.github/dependency-updates/README.md records the investigation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): count lock files as evidence a claimed bump landed

check_dependabot_claims.py only looked at .csproj PackageReference pins,
so a transitive-only update — a security bump to something no csproj
names — would have failed the check. That is the most costly PR to block
on a false positive.

Lock files are safe to trust here: in #130 the lock file recorded
Daqifi.Core at 1.3.0 right alongside the csproj, because the bump landed
nowhere. Re-verified against the repo's real Dependabot PRs — still
passes #93/#95/#96, still fails #130 on all ten unapplied claims.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): check a claimed bump in every project that pins it, not just one

Qodo, correctly: accepting a claim because SOME manifest reached the new
version lets a grouped multi-directory PR move one head and leave another
stale. Avalonia.Fonts.Inter is pinned by three projects here, so that is a
real input shape, not a hypothetical.

Now every project pinning the package must be at the claimed version —
scoped to the `directories` Dependabot actually manages, read from
dependabot.yml at check time. The scope matters in both directions: the
iOS head is not managed, so its pins drift on purpose and must not fail a
PR that could never have touched them; and if the list grows later, the
check grows with it. An unreadable config is exit 2, never a silently
widened scope.

Qodo's suggested fix — carrying per-claim directory context out of the PR
body — does not work: Dependabot dedupes a package across directories, so
a grouped body states each bump once regardless of how many directories
it touches.

Lock files stay evidence-only, never grounds for failure. A legitimate
single-directory PR leaves the other directories' lock files recording
the old transitive version (#93 changed only Daqifi.Avalonia/), so
failing on that would reject good PRs.

Re-verified against the repo's real Dependabot PRs: passes #93/#95/#96,
fails #130 on all ten unapplied claims, and the scope correctly excludes
the iOS head. Self-test grows to 28 cases, including Qodo's exact
scenario.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): measure Core drift from the oldest pin, not the first one read

Same class of bug Qodo found in the sibling guard: check_core_drift.py
returned the first manifest pinning Daqifi.Core and stopped. Two projects
pinning it at different versions would report whichever happened to be
read first — which could report "up to date" while another project sat
four releases back.

Drift is now measured from the oldest pin, with disagreeing pins called
out explicitly, and the verdict no longer depends on manifest order.
Today only Daqifi.Avalonia.csproj pins Core, so this changes nothing
about the current repo; it stops the guard going quiet the moment that
is no longer true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* style(ci): early-continue the lock-file walk instead of a wide ternary

Drops a 92-character line to within the file's own width; no behaviour
change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): keep every Daqifi.Core reference, not one per project file

Qodo, correctly: pinned_versions keyed by manifest path, so a second
reference to the package in the SAME project overwrote the first.
Conditional ItemGroups per framework or RID are an ordinary MSBuild
shape, and since drift is measured from the oldest pin, a newer
reference could hide an older one and report the repo current while a
conditional pin sat four releases back.

It now returns every (version, manifest) pair. Ties resolve by manifest
name rather than filesystem order, so the reported file is stable across
runs.

check_dependabot_claims.py does not share this: it keeps a set of
versions per package per file, so a duplicate reference at a stale
version already fails the claim.

Tests cover both orderings of a doubly-referenced project — the verdict
must not depend on which reference is read last.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tylerkron added a commit that referenced this pull request Aug 31, 2026
…at fixes them (#140)

* deps: document the lock-file commit every shared-library Dependabot PR needs, and script it

#139 left a decision open: the Android head is in Dependabot's `directories`
but Dependabot cannot refresh its packages.lock.json, so the choice was
between documenting a manual refresh and dropping the head the way the iOS
head is dropped. This takes the first option, because investigating it turned
up a bigger problem that decides the question.

Four of the five app lock files reach their packages through a
ProjectReference to Daqifi.Avalonia and record its full transitive closure.
Dependabot rewrites the lock file only in the directory whose manifest it
edited. So a one-line bump in the shared library invalidates five lock files
and refreshes one, and locked mode fails the other four with NU1004. That is
23 of the 28 packages managed here, and it is the current state of the repo:
#96 and #130 are both red on it now, on the Android and iOS heads alike.

That refutes the alternative. Dropping /Daqifi.Avalonia.Android would fix
nothing — its NU1004 comes from the ProjectReference closure, not from its own
two pins, and the iOS head proves it by already being out of `directories` and
failing identically. The guard offered as cover cannot help either:
avalonia-graph `needs: [desktop, android, ios]`, so it is SKIPPED on exactly
the PRs in question. Dropping the head would only cost the one automated watch
on Avalonia.Android.

Reproduced on the pinned SDK 10.0.302 by doing what Dependabot does — bump
Sentry 6.8.0 -> 6.9.0 in the shared library, refresh only its own lock file,
then restore each head in locked mode. All four fail NU1004; the new script
makes all four pass.

- refresh_lock_files.sh regenerates all six lock files. It refuses to do a
  partial job: wrong SDK or a missing android/ios workload exits 2 without
  writing, because a half-refresh looks finished and leaves the skipped head
  failing NU1004 anyway. $DOTNET is authoritative rather than one candidate
  among several. It iterates projects rather than the slnx, which does not
  contain AvaloniaCapture, and never passes -r, which would prune the lock
  file to one RID.
- test_refresh_lock_files.py drives it with a stub dotnet across four refusal
  cases and asserts nothing is written. Picked up by the existing `scripts`
  job, which globs test_*.py. Verified to fail when the refusal is removed.
- The README section that deferred this decision now records it, and the
  dependabot.yml comment says which PRs are affected before someone tries to
  merge one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): roll back the lock files when a refresh fails partway

Qodo review on #140, finding 2. The preflight refuses to start without the
pinned SDK and both workloads, but once restores begin they run one project at
a time — so a failure on the fourth left the first three already rewritten.
That is the same partial refresh the preflight exists to prevent, arriving by a
different door, and it looks like an ordinary refresh diff on the way out.

The script now snapshots every lock file before the first restore and rolls all
of them back on failure, exiting 1. The snapshot is of the WORKING TREE, not
HEAD: on a Dependabot branch the shared library's lock file is already
legitimately modified, and rolling back to HEAD would discard Dependabot's own
work along with ours.

Exit codes are now 0 refreshed / 1 a restore failed, rolled back / 2 could not
run, which lines up with the contract the other scripts here follow.

The self-test grows a rollback group: a stub dotnet that mutates each lock file
it restores and fails on the third. It carries a CONTROL case whose stub never
fails, because a rollback test whose stub never wrote anything would pass
without proving anything. Verified to fail when the rollback call is removed —
three lock files left modified.

Also records what the review's other finding got wrong. --force-evaluate IS
what overrides RestoreLockedMode, so CI=true does not block the refresh.
NuGet's own NU1004 text says so, and it is verified: same state, obj cleared,
CI=true, RestoreLockedMode evaluating true — a plain restore fails NU1004 and
the same restore with --force-evaluate regenerates the lock file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@tylerkron
tylerkron force-pushed the dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0 branch from 303acd6 to 3e7d767 Compare September 2, 2026 22:34
@tylerkron

Copy link
Copy Markdown
Contributor

Rebased onto main and pushed the lock-file commit Dependabot cannot produce.

The red CI was never about NCalc. Each head's packages.lock.json records the shared library's full transitive closure, and Dependabot rewrites only the lock file in the directory whose manifest it edited — so this one-line csproj change invalidated six lock files and shipped one, and the locked-mode restore in Directory.Build.props failed the other heads with NU1004. .github/scripts/refresh_lock_files.sh regenerates all six.

This is a major version, so the behaviour was measured, not assumed

NCalc is reached from exactly one place — AbstractChannel.ScaleExpression — and nothing in the repo tests it. That path builds new Expression(s) { Parameters = { ["x"] = 1 } }, calls Evaluate() once as a validity probe, then re-evaluates per sample and Convert.ToDouble()s the result.

A standalone probe replaying that exact sequence under 6.4.0 and 7.1.0 over 27 expressions produced byte-identical output — same values, same CLR types, same exception types on the invalid cases:

  • identity, affine (x*2+1, 2*x), x/2
  • (x - 32) * 5 / 9 — the integer-division trap, 100 under both
  • Pow Sqrt Abs Round Log Log10 Exp Max Min, if(x > 0, x, 0)
  • a bool-yielding comparison (x > 1True1), scientific notation (1e3 * x)
  • the non-finite cases the code comments call out: x / 0Infinity, 0 / 0NaN, neither throws under either version, so the double.IsFinite guard still carries them
  • invalid input: bogus(x)NCalcFunctionNotFoundException, x +NCalcParserException, y * 2NCalcParameterNotDefinedException — identical types, so HasValidExpression still goes false in the same three ways

The upgrade is behaviour-neutral for every expression this app can construct.

Other gates, on SDK 10.0.302 with CI=true

gate result
locked restore, all six projects succeeds, rewrites nothing
library / Desktop / AvaloniaCapture / iOS, Release build succeeded, 0 errors
lock files after the iossimulator-arm64 build untouched
unit tests 336/336 passed
check_dependabot_claims.py claim applied

No new warningsorigin/main measured in the same worktree, --no-incremental, is 246 / 246 / 247 / 247, and this branch reports the same four.

One thing worth saying plainly: the reason this needed a hand-built probe is that the scaling path has no test. That gap outlives this PR and belongs in its own change, not here.

🤖 Generated with Claude Code

@tylerkron
tylerkron added this pull request to the merge queue Sep 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 3, 2026
@tylerkron

Copy link
Copy Markdown
Contributor

@dependabot rebase

@dependabot @github

dependabot Bot commented on behalf of github Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Looks like this PR has been edited by someone other than Dependabot. That means Dependabot can't rebase it - sorry!

If you're happy for Dependabot to recreate it from scratch, overwriting any edits, you can request @dependabot recreate.

Rebased onto current main by hand: the original dependabot commit went
CONFLICTING once main's lock files moved underneath it, and `@dependabot
rebase` did not pick it up.

Regenerated all six packages.lock.json files with a plain `dotnet restore`
(no `-r`, so no RID narrowing and no NU1004 on CI's locked-mode restore).
The lock diff is NCalc-only: NCalcSync 6.4.0 -> 7.1.0, NCalc.Core/NCalc.Parser
replacing NCalc.Domain, Parlot and ExtendedNumerics.BigDecimal moving with it,
plus a transitive patch bump of Microsoft.Extensions.Logging.Abstractions
10.0.7 -> 10.0.10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tylerkron
tylerkron force-pushed the dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0 branch from 3e7d767 to 7fd2d74 Compare September 3, 2026 15:11
@tylerkron

Copy link
Copy Markdown
Contributor

Rebased this by hand onto current main (c89dd02) — head is now 7fd2d74 and the PR is
MERGEABLE. The original commit went CONFLICTING once main's lock files moved underneath
it, and @dependabot rebase did not pick it up.

What changed vs. the old head, nothing but a re-derivation of the same bump:

  • All six packages.lock.json files regenerated with a plain dotnet restoreno -r, so
    no RID narrowing and no NU1004 on CI's locked-mode restore. Verified the iOS lock still
    carries all three framework/RID sections (net10.0-ios26.5, /ios-arm64,
    /iossimulator-arm64), structurally identical to main.
  • The lock diff is NCalc-only and symmetric (121 insertions / 121 deletions): NCalcSync
    6.4.0 → 7.1.0, NCalc.Core + NCalc.Parser replacing NCalc.Domain, Parlot
    3003.0.0.346 → 3003.2.0.161, ExtendedNumerics.BigDecimal 1.5.7 → 1.5.8, plus a transitive
    patch bump of Microsoft.Extensions.Logging.Abstractions 10.0.7 → 10.0.10.

Gate, run locally on 7fd2d74: Desktop head green, iOS head green
(-c Release -r iossimulator-arm64 --no-restore), dotnet test 424/424. The Android head
cannot build on this Mac (only the ios workload is installed) — CI covers it.

The part that was not verifiable before

This is a major version bump of the library that evaluates user-entered channel scaling
expressions
— the x * 1.5 a user types, applied to every sample before it reaches the live
plot, the logging database and the CSV export. That path had no tests at all, so a green
build could not tell you whether a recorded reading comes out the same on 7.1.0. It only tells
you the app still compiles.

So I wrote characterisation tests for it and ran them against both versions from one working
tree, changing only the PackageReference with a plain restore between: 26/26 pass on 6.4.0
and 26/26 pass on 7.1.0, identically.
They cover the arithmetic operators in double precision,
the built-in functions (Abs/Sqrt/Pow/Round/Max/Min), acceptance and rejection at
entry, the non-finite guard that keeps Infinity/NaN out of recorded data, and — the one with
real teeth — that a decimal literal still parses with a dot under a comma-decimal culture, since
a culture-sensitive parser would turn x * 1.5 into x * 15 for a de-DE user with no error
message anywhere.

Those tests are in #221, separately, so they land whether or not this bump does and guard the
next one too. Worth merging #221 first — then this PR's own CI exercises the new tests
against 7.1.0 rather than my local run being the only evidence.

Caveat that survives: the guarantee is coverage-shaped, not absolute. Every NCalc construct
reachable from ScaleExpression today is covered; a 7.x semantic change in some other NCalc
feature the app does not currently use would go unnoticed.

@tylerkron
tylerkron added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 52835ff Sep 3, 2026
7 checks passed
@tylerkron
tylerkron deleted the dependabot/nuget/Daqifi.Avalonia/NCalcSync-7.1.0 branch September 3, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant