Repository navigation
Replace #132: pack fixtures, fix CI, remove hash pinning - #133
Conversation
This is the final tree of codex/production-readiness-pr (#132, head feb8e88), except for the 631 regression corpora of 1 MB or more under tests/fixtures/library (5.91 GB, seven of them in Git LFS). The next commit adds them back as xz archives. The code was produced by Codex and is carried over unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Commit the 631 corpora as <name>.json.xz (14.2 MB in all) instead of 5.91 GB of JSON and LFS objects. `python3 tools/fixtures.py materialize` writes the JSON beside each archive; the copies are Git-ignored. - Read those corpora at run time through tests/fixtures/packed.rs instead of include_str!, so test and example binaries no longer embed them. Shared contract files that nest inside one crate allow clippy::duplicate_mod on its module line. - CI: materialize instead of fetching LFS, free runner disk in the two heavy Linux jobs, build them with CARGO_PROFILE_DEV_DEBUG=0, and pass Windows-style binary paths in the Excel job. - extended.rs uses the quote literals already in cases.json instead of recomputing them with exp_m1, whose last digit differs on MSVC. - Release: publish convex-rates and convex-credit before convex-analytics. - Docs: describe materialize instead of git lfs pull. No financial code, fixture value, expected result, tolerance or solver default changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The assurance tooling pinned the SHA-256 of nearly every protected file and required a review row with fresh hashes for every edit. This removes it: - Delete tools/assurance/change-reviews.json, numeric-evidence.json and tools/source_pins.py, and the checks that read them. - Drop sha256 from the tools/assurance/inventory.json rows and from the comparison catalog and pack artifact references. - Remove tests/fixtures/packed.json: the .json.xz archives are the source of truth, and xz's CRC64 rejects a corrupt one. - tools/library/library-inventory.json lists inspected_sources as paths; observation anchors are still checked against the current files. Kept: the structural checks (forbidden dependencies, lints, lifecycle purity, required CI steps, inventory paths, exception expiry), the solver default check, the required comparator files, and the --base checks that reject wider tolerances or removed cases, measures and coverage. Hashes recorded inside fixture data and capture manifests are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WalkthroughWarning Review details and warnings were omitted to fit the comment limit. |
There was a problem hiding this comment.
44 issues found across 2713 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="crates/convex-ffi/build.rs">
<violation number="1" location="crates/convex-ffi/build.rs:27">
P2: `generate()?` makes optional C-header generation a hard prerequisite for the cdylib and Java build, although the Java binding does not consume this header. Preserve the warning/skip path (or isolate header generation from the library build) so a cbindgen parser failure cannot block otherwise valid FFI tests and artifacts.</violation>
</file>
<file name="crates/convex-wasm/src/conventions.rs">
<violation number="1" location="crates/convex-wasm/src/conventions.rs:190">
P1: This exact lookup makes many valid dropdown selections fail to load defaults, including `Eurozone/GovernmentBond`; restore the market fallback for pairs without specific rules. The UI keeps the previous defaults when this call returns an error, so changing to such a pair can apply stale conventions.</violation>
</file>
<file name="crates/convex-analytics/src/spreads/ispread.rs">
<violation number="1" location="crates/convex-analytics/src/spreads/ispread.rs:140">
P1: `as_decimal_f64()` already returns the decimal rate, so this extra division makes nonzero implied I-spreads 10,000 times too small. Remove the `/ 10_000.0` suffix.</violation>
</file>
<file name="crates/convex/examples/library_curve_methods.rs">
<violation number="1" location="crates/convex/examples/library_curve_methods.rs:11">
P3: This example duplicates the complete consumer runner used by the sibling library examples, so fixes to envelope validation or CLI output must be copied across every binary. Extract the shared runner and pass the contract-specific cases and qualification text into it.</violation>
</file>
<file name="crates/convex-math/src/bounds.rs">
<violation number="1" location="crates/convex-math/src/bounds.rs:34">
P2: `interval` rejects finite endpoints whenever `b - a` overflows, so `bisection` cannot solve valid brackets such as `[-f64::MAX, f64::MAX]` despite using an overflow-safe midpoint. Validate the arithmetic required by each caller instead of requiring a finite raw width.</violation>
</file>
<file name="crates/convex-math/src/interpolation/mod.rs">
<violation number="1" location="crates/convex-math/src/interpolation/mod.rs:37">
P3: `MonotoneConvex::new` accepts zero interval forwards, but this new summary still promises positive forwards. Change the bullet to `Nonnegative forward rates`.</violation>
</file>
<file name="crates/convex-core/src/types/quote_display.rs">
<violation number="1" location="crates/convex-core/src/types/quote_display.rs:24">
P2: This truncates quotes instead of preserving the previous four-place rounding contract. Restore four-place rounding, including carry and banker's-half behavior, before emitting the display string; otherwise prices and yields can be shown one tick below their rounded quote.</violation>
</file>
<file name="crates/convex-bonds/src/instruments/callable_frn.rs">
<violation number="1" location="crates/convex-bonds/src/instruments/callable_frn.rs:40">
P2: This exposes `ParCall` schedules with non-par redemption prices through `contract_terms`; reject non-100 prices or otherwise enforce par pricing before converting the schedule to exercise terms.</violation>
</file>
<file name="crates/convex-ffi/README.md">
<violation number="1" location="crates/convex-ffi/README.md:54">
P2: The C surface list omits the newly exported `convex_library` RPC. Add it to this declaration so C clients can discover the versioned supplied-input API documented by the implementation.</violation>
<violation number="2" location="crates/convex-ffi/README.md:177">
P2: This says that YAS outputs were removed, although `convex_yas` still returns a full `YasResponse`. Say that the obsolete YAS government/benchmark spread fields were removed instead, so callers do not incorrectly delete YAS handling.</violation>
</file>
<file name="crates/convex-analytics/src/risk/convexity/analytical.rs">
<violation number="1" location="crates/convex-analytics/src/risk/convexity/analytical.rs:13">
P2: This delegation regresses valid settlement-time cash flows: `analytical_convexity(&[0.0], &[100.0], ...)` now errors instead of returning zero convexity. Preserve zero-time handling for this numerical-time API, or route it through a validation path that permits zero times.</violation>
</file>
<file name="crates/convex-analytics/src/risk/duration/macaulay.rs">
<violation number="1" location="crates/convex-analytics/src/risk/duration/macaulay.rs:13">
P2: This delegation removes the near-zero price guard, so nearly cancelling signed cash flows produce an extreme unstable duration instead of `CalculationFailed`. Preserve the existing `abs(price) < 1e-10` check before converting `risk.macaulay_duration`.</violation>
</file>
<file name="crates/convex-mcp/src/server.rs">
<violation number="1" location="crates/convex-mcp/src/server.rs:789">
P1: This full-library call runs synchronously on a Tokio worker, so concurrent calibration, hedge, or lifecycle requests can occupy all workers and stall the MCP server. Run the bounded CPU-heavy operation through `spawn_blocking` before returning the tool result.</violation>
</file>
<file name="crates/convex-bonds/src/options/hw1f/mod.rs">
<violation number="1" location="crates/convex-bonds/src/options/hw1f/mod.rs:86">
P2: `b` can return zero for valid subnormal mean-reversion values when `a * time` underflows, instead of the Ho–Lee limit `time`. Treat an underflowed product as the zero-mean-reversion case so bond-option and lattice calculations retain the correct limit.</violation>
</file>
<file name="crates/convex/examples/child_cash_pnl.rs">
<violation number="1" location="crates/convex/examples/child_cash_pnl.rs:1">
P3: The module comment mislabels this example as independently owned invoice cash. The cases exercise acquired-child coupon/principal entitlements, so rename the comment to avoid documenting the wrong financial behavior.</violation>
</file>
<file name="crates/convex-math/src/interpolation/cubic_spline.rs">
<violation number="1" location="crates/convex-math/src/interpolation/cubic_spline.rs:106">
P2: Exact-knot queries can fail for valid finite coordinates when a segment width exceeds `sqrt(f64::MAX)`: the formula creates `NaN` from `0 * (h * h)` before this check. Return the stored knot value for exact knots, or otherwise avoid forming the overflowing squared width.</violation>
</file>
<file name="crates/convex-bonds/src/types/amortization.rs">
<violation number="1" location="crates/convex-bonds/src/types/amortization.rs:142">
P2: Empty non-`Bullet` schedules are accepted and converted into full-face bullet profiles, even though those amortization types require a schedule. Reject empty entries for non-`Bullet` types (the `PassThrough` case is already handled separately).</violation>
</file>
<file name="crates/convex-analytics/src/quote_risk/optional.rs">
<violation number="1" location="crates/convex-analytics/src/quote_risk/optional.rs:28">
P1: This admission can relabel an optional bond's native PV and sensitivity into a caller-supplied currency, and it accepts non-decimal OAS units while passing decimal bumps to the pricer. Require the position and factor currency to match `prepared.currency()` and require `QuoteUnit::DecimalRate` before constructing the native certificates.</violation>
<violation number="2" location="crates/convex-analytics/src/quote_risk/optional.rs:42">
P1: Validate optional position semantics before scaling: accept only original-face `Notional` or unit `TradeCopies`, matching the native PV's reference size. The current ratio-only check lets arbitrary reference quantities produce materially mis-scaled OAS risk.</violation>
</file>
<file name="crates/convex-analytics/src/risk/var/mod.rs">
<violation number="1" location="crates/convex-analytics/src/risk/var/mod.rs:77">
P2: `product` rejects valid parametric inputs when an early factor underflows before a later exposure rescales it. Accumulate the product in a numerically stable order or track its scale before rejecting only a non-representable final result.</violation>
</file>
<file name="crates/convex-analytics/src/risk/duration/key_rate.rs">
<violation number="1" location="crates/convex-analytics/src/risk/duration/key_rate.rs:52">
P3: These new validation errors are rendered as `at most4096key...` and `1..4096key...`, which makes API failures difficult to read. Add spaces around the bounds: `at most 4096 key rates required` and `1..4096 key rate tenors required`.</violation>
</file>
<file name="crates/convex-analytics/src/workflow/optional_hedge.rs">
<violation number="1" location="crates/convex-analytics/src/workflow/optional_hedge.rs:48">
P1: This admission can accept a prepared bond built from different materialized curve nodes when its recipes match the graph, then calculate every hedge column against the prepared curves instead of `graph.baseline().curves`. Also call `prepared.admit_scenario_baseline(&graph.baseline().curves)` before pricing factors.</violation>
</file>
<file name="crates/convex-bonds/src/traits/bond.rs">
<violation number="1" location="crates/convex-bonds/src/traits/bond.rs:237">
P1: This default loses the underlying fixed bond’s schedule context for `CallableBond`, so generic yield, price, and risk analytics can either fail for contextual day counts or use incorrect settlement-to-payment times. Override `yield_cash_flows` on `CallableBond` to delegate to the base bond’s schedule-aware route.</violation>
</file>
<file name="crates/convex-analytics/src/cash_calibration/reprice.rs">
<violation number="1" location="crates/convex-analytics/src/cash_calibration/reprice.rs:64">
P2: `ReferenceZeroSpread` repricing accepts a reference curve with a different origin or time basis, causing discount factors to be evaluated on a different timeline than the issuer basis. Reject references whose `reference_date` and `time_basis` differ from `expected` before pricing.</violation>
</file>
<file name="crates/convex-server/src/configuration_admin.rs">
<violation number="1" location="crates/convex-server/src/configuration_admin.rs:70">
P2: `effective_configuration` serializes the full document and its full assembly, duplicating potentially almost 2 MiB of curve data. Production access middleware caps JSON responses at 4 MiB and converts oversized responses to `CONFIGURATION_UNAVAILABLE`, so valid near-limit configurations cannot be inspected; omit the duplicate payload or give this endpoint an appropriate bounded response budget.</violation>
</file>
<file name="crates/convex-analytics/src/spreads/government_curve.rs">
<violation number="1" location="crates/convex-analytics/src/spreads/government_curve.rs:306">
P2: Validate the requested maturity horizon before interpolation; otherwise `supplied_yield` accepts targets beyond the repository’s 150-year contract span and extrapolates them instead of returning `InvalidInput`.</violation>
<violation number="2" location="crates/convex-analytics/src/spreads/government_curve.rs:315">
P1: `with_extrapolation()` is linear, so out-of-range target maturities do not use the documented flat endpoint yield. Clamp the query to the first/last point before interpolation (or implement explicit flat endpoint handling).</violation>
</file>
<file name="crates/convex-math/src/optimization/mod.rs">
<violation number="1" location="crates/convex-math/src/optimization/mod.rs:27">
P2: This new guard breaks the compatibility function for reversed bounds: the previous implementation converged for `a > b`, but the wrapper now returns `NaN`. Accept reversed endpoints by normalizing them or by removing this order-only rejection.</violation>
</file>
<file name="crates/convex-wasm/src/ingress.rs">
<violation number="1" location="crates/convex-wasm/src/ingress.rs:41">
P2: `snapshot_record` rejects valid plain objects created in another realm because `Object.prototype` identity is realm-specific. Use a realm-independent plain-object check while retaining the own-key snapshot.</violation>
<violation number="2" location="crates/convex-wasm/src/ingress.rs:75">
P2: `decode_curve_points` calls `Array::from` before enforcing the 4096 limit, so large sparse arrays can allocate before rejection and custom iterators can replace the points. Read the original length first, then fetch each numeric index and require an own property.</violation>
<violation number="3" location="crates/convex-wasm/src/ingress.rs:77">
P3: The public curve-point error message is missing spaces. Change it to `Curve point count must be 1..4096`.</violation>
</file>
<file name="crates/convex-bonds/src/options/swaption_hw1f.rs">
<violation number="1" location="crates/convex-bonds/src/options/swaption_hw1f.rs:101">
P2: `try_payer_swaption_hw1f` accepts non-integral `tail` values but rounds them to `n`, pricing payments beyond the requested maturity. Reject non-integral annual tails before constructing `cs`.</violation>
<violation number="2" location="crates/convex-bonds/src/options/swaption_hw1f.rs:146">
P2: The checked pricer rejects valid high-strike swaptions when the Jamshidian root lies outside the hard-coded `[-1, 1]` bracket. Expand the bracket adaptively or validate and document a strike/rate domain that guarantees this interval.</violation>
</file>
<file name="crates/convex/examples/library_host_census.rs">
<violation number="1" location="crates/convex/examples/library_host_census.rs:21">
P2: Apply the shared 16 MiB response limit to the native envelope before comparing it with `execute_json`; otherwise large valid cases make the host-census generation fail instead of retaining the shared transport outcome.</violation>
</file>
<file name="crates/convex/examples/cash_calibration.rs">
<violation number="1" location="crates/convex/examples/cash_calibration.rs:56">
P2: The credit branch never compares `fitted.annualized_parameters` with `credit_case.expected_parameters`, so a calibration-parameter regression can still print success. Check the expected parameter vector as the cash calibration tests do.</violation>
<violation number="2" location="crates/convex/examples/cash_calibration.rs:63">
P2: The credit check discards `basis`, validating only aggregate bond price and cash projection. A wrong promised/recovery PV decomposition can therefore pass despite the fixture’s independent recovery expectations; retain `basis` and compare both PV components.</violation>
</file>
<file name="crates/convex-mcp/README.md">
<violation number="1" location="crates/convex-mcp/README.md:71">
P3: The README's Tools table omits the newly added `library` and `library_schema` MCP tools, so users cannot discover the supplied-input API from the server's tool inventory. Add rows for both tools.</violation>
</file>
<file name="crates/convex-curves/src/lib.rs">
<violation number="1" location="crates/convex-curves/src/lib.rs:234">
P3: This method now documents a nonnegative guarantee but is still named `guarantees_positive_forwards`, which can mislead callers into assuming strictly positive forwards. Rename it to reflect nonnegativity, or add a correctly named API and deprecate the ambiguous name.</violation>
</file>
<file name="crates/convex-math/src/extrapolation/mod.rs">
<violation number="1" location="crates/convex-math/src/extrapolation/mod.rs:62">
P2: `try_extrapolate` accepts targets before `last_t`, so linear extrapolation silently runs backward despite this API being for extending beyond the last point. Reject `t < last_t` here.</violation>
</file>
<file name="crates/convex-ffi/src/build.rs">
<violation number="1" location="crates/convex-ffi/src/build.rs:181">
P2: `business_day_convention` is applied only for fixed-rate bonds, although `CouponSpec` is shared by callable and sinking-fund specs. Forward this field when building those underlying fixed bonds too; otherwise identical JSON terms use different payment schedules by bond type.</violation>
</file>
<file name="crates/convex-analytics/src/pricing.rs">
<violation number="1" location="crates/convex-analytics/src/pricing.rs:66">
P2: The new issue-date validation covers `price_from_mark`, but callable OAS marks bypass it and accept settlements before issuance. Add the same `settlement < bond.issue_date()` check to the OAS branch before pricing.</violation>
</file>
<file name="crates/convex-ports/src/issuer_reference.rs">
<violation number="1" location="crates/convex-ports/src/issuer_reference.rs:24">
P3: `IssuerScopeDefinition::validate` does not validate the scope’s own effective interval or provenance, so direct callers can accept malformed issuer scopes. Validate those fields here, not only in the outer `MasterDefinition` path.</violation>
</file>
<file name="crates/convex-analytics/src/credit_calibration/reprice.rs">
<violation number="1" location="crates/convex-analytics/src/credit_calibration/reprice.rs:30">
P2: `validate_recipe` checks only that each discount dependency exists, so this repricer can use a same-key curve with incompatible metadata. Validate the supplied discount dependency’s full recipe before calling `price`; otherwise residuals may be computed against the wrong discounting definition.</violation>
</file>
<file name="bindings/java/src/main/java/com/convex/ConvexAnalytics.java">
<violation number="1" location="bindings/java/src/main/java/com/convex/ConvexAnalytics.java:96">
P2: The typed Java API still exposes `G_SPREAD`, but this change removes its only benchmark input and adds no `params.government` replacement. G-spread calls therefore fail with the native “actual government benchmarks” error; add a government-benchmark DTO/builder or explicitly remove/guard this typed operation.</violation>
</file>
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Tip: Get a Slack DM when cubic finishes reviewing your PRs. Connect Slack
View guided diff | Re-trigger cubic
| let rules = registry | ||
| .rules(&key) | ||
| .ok_or("No registered convention rules for this market/instrument pair")?; |
There was a problem hiding this comment.
P1: This exact lookup makes many valid dropdown selections fail to load defaults, including Eurozone/GovernmentBond; restore the market fallback for pairs without specific rules. The UI keeps the previous defaults when this call returns an error, so changing to such a pair can apply stale conventions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-wasm/src/conventions.rs, line 190:
<comment>This exact lookup makes many valid dropdown selections fail to load defaults, including `Eurozone/GovernmentBond`; restore the market fallback for pairs without specific rules. The UI keeps the previous defaults when this call returns an error, so changing to such a pair can apply stale conventions.</comment>
<file context>
@@ -184,26 +172,30 @@ pub fn get_convention_options() -> JsValue {
-
- let defaults = DefaultConventions {
- day_count: format!("{}", rules.accrual_day_count),
+ let rules = registry
+ .rules(&key)
+ .ok_or("No registered convention rules for this market/instrument pair")?;
</file context>
| let rules = registry | |
| .rules(&key) | |
| .ok_or("No registered convention rules for this market/instrument pair")?; | |
| let rules = registry | |
| .rules(&key) | |
| .cloned() | |
| .unwrap_or_else(|| registry.default_rules_for_market(market_enum)); |
| @@ -129,7 +137,7 @@ impl<'a> ISpreadCalculator<'a> { | |||
| /// Implied bond yield. | |||
| pub fn implied_yield(&self, i_spread: Spread, maturity: Date) -> AnalyticsResult<Yield> { | |||
| let swap_rate = self.swap_rate_at_maturity(maturity)?; | |||
| let spread_decimal = i_spread.as_decimal().to_f64().unwrap_or(0.0) / 10_000.0; | |||
| let spread_decimal = i_spread.as_decimal_f64()? / 10_000.0; | |||
There was a problem hiding this comment.
P1: as_decimal_f64() already returns the decimal rate, so this extra division makes nonzero implied I-spreads 10,000 times too small. Remove the / 10_000.0 suffix.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-analytics/src/spreads/ispread.rs, line 140:
<comment>`as_decimal_f64()` already returns the decimal rate, so this extra division makes nonzero implied I-spreads 10,000 times too small. Remove the `/ 10_000.0` suffix.</comment>
<file context>
@@ -129,7 +137,7 @@ impl<'a> ISpreadCalculator<'a> {
pub fn implied_yield(&self, i_spread: Spread, maturity: Date) -> AnalyticsResult<Yield> {
let swap_rate = self.swap_rate_at_maturity(maturity)?;
- let spread_decimal = i_spread.as_decimal().to_f64().unwrap_or(0.0) / 10_000.0;
+ let spread_decimal = i_spread.as_decimal_f64()? / 10_000.0;
let implied = swap_rate + spread_decimal;
</file context>
| let spread_decimal = i_spread.as_decimal_f64()? / 10_000.0; | |
| let spread_decimal = i_spread.as_decimal_f64()?; |
| Parameters(params): Parameters<LibraryToolParams>, | ||
| ) -> Result<CallToolResult, McpError> { | ||
| Ok(CallToolResult::success(vec![rmcp::model::Content::text( | ||
| convex::library::execute_json(¶ms.request_json), |
There was a problem hiding this comment.
P1: This full-library call runs synchronously on a Tokio worker, so concurrent calibration, hedge, or lifecycle requests can occupy all workers and stall the MCP server. Run the bounded CPU-heavy operation through spawn_blocking before returning the tool result.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-mcp/src/server.rs, line 789:
<comment>This full-library call runs synchronously on a Tokio worker, so concurrent calibration, hedge, or lifecycle requests can occupy all workers and stall the MCP server. Run the bounded CPU-heavy operation through `spawn_blocking` before returning the tool result.</comment>
<file context>
@@ -770,9 +767,35 @@ pub struct ListOutput<T> {
+ Parameters(params): Parameters<LibraryToolParams>,
+ ) -> Result<CallToolResult, McpError> {
+ Ok(CallToolResult::success(vec![rmcp::model::Content::text(
+ convex::library::execute_json(¶ms.request_json),
+ )]))
+ }
</file context>
| convex::library::execute_json(¶ms.request_json), | |
| tokio::task::spawn_blocking(move || { | |
| convex::library::execute_json(¶ms.request_json) | |
| }) | |
| .await | |
| .map_err(|e| McpError::internal_error(e.to_string(), None))?, |
| "single-currency model OAS and reference PV currency must agree", | ||
| )); | ||
| } | ||
| position.scale()?; |
There was a problem hiding this comment.
P1: Validate optional position semantics before scaling: accept only original-face Notional or unit TradeCopies, matching the native PV's reference size. The current ratio-only check lets arbitrary reference quantities produce materially mis-scaled OAS risk.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-analytics/src/quote_risk/optional.rs, line 42:
<comment>Validate optional position semantics before scaling: accept only original-face `Notional` or unit `TradeCopies`, matching the native PV's reference size. The current ratio-only check lets arbitrary reference quantities produce materially mis-scaled OAS risk.</comment>
<file context>
@@ -0,0 +1,77 @@
+ "single-currency model OAS and reference PV currency must agree",
+ ));
+ }
+ position.scale()?;
+ super::finite::validate_settings(&key, &settings)?;
+ let native = prepared.risk(oas_decimal, RiskAxis::Oas, settings.bump, native_settings)?;
</file context>
| policies: q::OptionalRiskPolicies, | ||
| ) -> WorkflowResult<OptionalGraphHedgeRisk> { | ||
| super::hedge::validate_graph_hedge_input(input)?; | ||
| q::admit_optional_graph(prepared, graph)?; |
There was a problem hiding this comment.
P1: This admission can accept a prepared bond built from different materialized curve nodes when its recipes match the graph, then calculate every hedge column against the prepared curves instead of graph.baseline().curves. Also call prepared.admit_scenario_baseline(&graph.baseline().curves) before pricing factors.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-analytics/src/workflow/optional_hedge.rs, line 48:
<comment>This admission can accept a prepared bond built from different materialized curve nodes when its recipes match the graph, then calculate every hedge column against the prepared curves instead of `graph.baseline().curves`. Also call `prepared.admit_scenario_baseline(&graph.baseline().curves)` before pricing factors.</comment>
<file context>
@@ -0,0 +1,113 @@
+ policies: q::OptionalRiskPolicies,
+) -> WorkflowResult<OptionalGraphHedgeRisk> {
+ super::hedge::validate_graph_hedge_input(input)?;
+ q::admit_optional_graph(prepared, graph)?;
+ let position = &input.reference_position;
+ if position.quantity_unit != q::RiskQuantityUnit::Notional
</file context>
| @@ -5,7 +5,9 @@ | |||
|
|
|||
There was a problem hiding this comment.
P3: These new validation errors are rendered as at most4096key... and 1..4096key..., which makes API failures difficult to read. Add spaces around the bounds: at most 4096 key rates required and 1..4096 key rate tenors required.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-analytics/src/risk/duration/key_rate.rs, line 52:
<comment>These new validation errors are rendered as `at most4096key...` and `1..4096key...`, which makes API failures difficult to read. Add spaces around the bounds: `at most 4096 key rates required` and `1..4096 key rate tenors required`.</comment>
<file context>
@@ -44,16 +46,60 @@ impl KeyRateDurations {
+ pub fn total_duration(&self) -> AnalyticsResult<Duration> {
+ if self.durations.len() > 4096 {
+ return Err(AnalyticsError::InvalidInput(
+ "at most4096key rates required".into(),
+ ));
+ }
</file context>
| -v, --verbose Enable verbose logging | ||
| -h, --help Print help | ||
| -V, --version Print version | ||
| ``` | ||
|
|
||
| ## Full supplied-input library API |
There was a problem hiding this comment.
P3: The README's Tools table omits the newly added library and library_schema MCP tools, so users cannot discover the supplied-input API from the server's tool inventory. Add rows for both tools.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-mcp/README.md, line 71:
<comment>The README's Tools table omits the newly added `library` and `library_schema` MCP tools, so users cannot discover the supplied-input API from the server's tool inventory. Add rows for both tools.</comment>
<file context>
@@ -59,7 +62,15 @@ Options:
-V, --version Print version
+## Full supplied-input library API
+
+See the shared API, exact terms and capability limits.
</file context>
</details>
| } | ||
| let points = Array::from(&value); | ||
| if points.length() == 0 || points.length() > 4096 { | ||
| return Err("Curve point count must be1..4096".into()); |
There was a problem hiding this comment.
P3: The public curve-point error message is missing spaces. Change it to Curve point count must be 1..4096.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-wasm/src/ingress.rs, line 77:
<comment>The public curve-point error message is missing spaces. Change it to `Curve point count must be 1..4096`.</comment>
<file context>
@@ -0,0 +1,125 @@
+ }
+ let points = Array::from(&value);
+ if points.length() == 0 || points.length() > 4096 {
+ return Err("Curve point count must be1..4096".into());
+ }
+ points
</file context>
| return Err("Curve point count must be1..4096".into()); | |
| return Err("Curve point count must be 1..4096".into()); |
| } | ||
|
|
||
| impl InterpolationMethod { | ||
| /// Returns true if this method guarantees positive forward rates. | ||
| /// Returns true if the method's correctly represented admissible input |
There was a problem hiding this comment.
P3: This method now documents a nonnegative guarantee but is still named guarantees_positive_forwards, which can mislead callers into assuming strictly positive forwards. Rename it to reflect nonnegativity, or add a correctly named API and deprecate the ambiguous name.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-curves/src/lib.rs, line 234:
<comment>This method now documents a nonnegative guarantee but is still named `guarantees_positive_forwards`, which can mislead callers into assuming strictly positive forwards. Rename it to reflect nonnegativity, or add a correctly named API and deprecate the ambiguous name.</comment>
<file context>
@@ -217,10 +224,16 @@ pub enum InterpolationMethod {
impl InterpolationMethod {
- /// Returns true if this method guarantees positive forward rates.
+ /// Returns true if the method's correctly represented admissible input
+ /// domain guarantees nonnegative forwards. This does not validate an
+ /// arbitrary raw-value representation or certify a market curve.
</file context>
| impl IssuerScopeDefinition { | ||
| /// Bounded structural and source-time admission; no pricing or recovery estimate. | ||
| pub fn validate(&self, recorded_at: Option<UtcTimestamp>) -> Result<(), ReferenceError> { | ||
| self.directory |
There was a problem hiding this comment.
P3: IssuerScopeDefinition::validate does not validate the scope’s own effective interval or provenance, so direct callers can accept malformed issuer scopes. Validate those fields here, not only in the outer MasterDefinition path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At crates/convex-ports/src/issuer_reference.rs, line 24:
<comment>`IssuerScopeDefinition::validate` does not validate the scope’s own effective interval or provenance, so direct callers can accept malformed issuer scopes. Validate those fields here, not only in the outer `MasterDefinition` path.</comment>
<file context>
@@ -0,0 +1,54 @@
+impl IssuerScopeDefinition {
+ /// Bounded structural and source-time admission; no pricing or recovery estimate.
+ pub fn validate(&self, recorded_at: Option<UtcTimestamp>) -> Result<(), ReferenceError> {
+ self.directory
+ .validate()
+ .map_err(|_| ReferenceError::InvalidInput("invalid issuer directory"))?;
</file context>
- Offline Quantitative Comparison never materialized the packed fixtures, so the swap capture tests could not read calibration-replay.json. It now materializes them, and frees runner disk and drops debuginfo like the other jobs that hold the unpacked corpora. - The Excel tests checked CX.LIBRARY.RANGE output by rejoining it with LibraryCells.Join, which applies the 2 MiB request bound. Fourteen P&L responses are larger (the native response bound is 16 MiB), so the tests now rejoin output with ResponseCells.Join, which checks cell sizes only. - LibraryCells.ValidateInput leaked EncoderFallbackException for an unpaired surrogate; it now throws the ArgumentException the add-in and its test expect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bounded Rust Assurance failed at "Check independently declared optional cash model and hedge controls". The optional graph hedge reference, qualification and calibrated generators call the platform libm (expm1, erfc, exp), so their regenerated floats differ in the last digits on Linux and on Windows, and both differ from the committed controls. Their byte-exact check can pass only on the machine that wrote them. Regenerating the controls would cascade: their bytes, and the reference script's own bytes, are recorded as source reads in other fixtures. So the generators and controls stay unchanged, and the three CI steps now run tests/scripts/optional_graph_hedge_controls.py. It regenerates the same six files and requires structure, text (including source reads) and integers to match exactly. Floats must agree within 1e-11 absolute, or 1e-13 relative above magnitude 100. The largest drift is 1.4e-12 on Linux and 2.8e-12 on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces #132. It carries the same fixed-income workflow work as #132, rebuilt on
mainso that 5.9 GB of JSON fixtures never enter Git history. It also fixes the problems that kept #132's CI red, and removes the hash pinning that made every edit to the repository need a matching hash refresh.What was wrong with #132
include_str!sites embedded 60–180 MB corpora into hundreds of test and example binaries, on top of the checkout. Both jobs died withNo space left on device. Most of the 935 assurance steps never ran.curve.fit.datedcame fromexp_m1. MSVC's libm returns a result one ulp away from glibc's, so the emitted request no longer matchedcases.json.--binary target/excel/examples/X.exeto Pythonsubprocess. WindowsCreateProcessrejects forward-slash relative paths. The job never got this far before.convex-analyticsnow depends on the newconvex-ratesandconvex-creditcrates, butrelease.ymlstill published only core, math, curves, bonds and analytics.numeric-evidence.json,inventory.json,library-inventory.json, the comparison catalog) and required achange-reviews.jsonrow with fresh hashes for each change. Any fix, even a clippy one, failed CI until those were refreshed.Commits
feb8e889), unchanged except that the 631 large fixtures are left out.<name>.json.xz: 14.2 MB in total, down from 5.91 GB. Git LFS is no longer used.python3 tools/fixtures.py materializewrites the JSON beside each archive (about 80 s); the written files are Git-ignored.packrefreshes an archive after a deliberate regeneration.include_str!sites that pointed at packed corpora, in 56 test and contract files, now callpacked::text(...)fromtests/fixtures/packed.rs. Each file is read once per process. Nothing else in those files changes; I verified this token by token.extended.rsuses the two quote literals already incases.jsoninstead of recomputing them withexp_m1. The Excel job passes Windows-style binary paths.CARGO_PROFILE_DEV_DEBUG=0: with line tables only, the workspace test build passed 32 GB before running a single test.convex-ratesandconvex-creditare published beforeconvex-analytics.docs/TEST-DATA.mdandREADME.mddescribematerializeinstead ofgit lfs pull.tools/assurance/change-reviews.json,numeric-evidence.jsonandtools/source_pins.py, and the checks that read them.sha256from theinventory.jsonrows and from the comparison catalog and pack references.library-inventory.jsonlistsinspected_sourcesas paths; its observation anchors are still checked against the current files.tests/fixtures/packed.json. The archives are the source of truth, and xz's CRC64 rejects a corrupt one.materializeandcheckalso requiretests/fixtures/.gitignoreto match the archives.DEFAULT_TOLERANCE1e-10,DEFAULT_MAX_ITERATIONS100) and the required comparator and reference files.--basechecks that reject wider tolerances and removed cases, measures or library coverage.CX.LIBRARY.RANGEoutput by rejoining it withLibraryCells.Join, which applies the 2 MiB request bound. Fourteen P&L responses are larger; the native response bound is 16 MiB. The tests now rejoin output with a test helper,ResponseCells.Join, that checks cell sizes only. The request-side round trips are unchanged.LibraryCells.ValidateInputletEncoderFallbackExceptionescape for an unpaired surrogate. It now throws the plainArgumentExceptionits test expects.expm1,erfc,exp). Their regenerated floats differ in the last digits on Linux and on Windows, and both differ from Codex's committed controls, so their byte-exact checks can pass only on the machine that wrote them.tests/scripts/optional_graph_hedge_controls.py. It regenerates the same six files, requires structure, text (including source reads) and integers to match exactly, and allows floats 1e-11 absolute, or 1e-13 relative above magnitude 100. The largest drift is 1.4e-12 on Linux and 2.8e-12 on Windows; the tightest consumer tolerance in the controls is 1e-10 (cash).No financial code, fixture value, expected result, tolerance or solver default changes. The only assertion changes are the Excel output round trips in commit 4 and the three optional-graph-hedge control checks in commit 5. #132's CI never reached
dotnet test, so this PR is the first run of those tests.Verification
Linux (Ubuntu under WSL 2):
The full workspace suite (
cargo test --locked --all-features --workspace --no-fail-fast -- --test-threads=1,CARGO_PROFILE_DEV_DEBUG=0) gave 3,351 passed, 0 failed, 78 ignored. It ran before the clippy fixes inpacked.rsand the contract files' module lines. After those fixes, I re-rancargo fmt --check,cargo clippy --locked --workspace --all-targets --all-features -- -D warnings(0 warnings) and the fixture-reading tests (monotone_contracts,child_maturity,concurrent_claims,custody_lineage,date_rolls).A regeneration probe found that 344 generator outputs reproduce the fixture bytes exactly.
At the final head:
tools/fixtures.py checkandreconciliation/comparison.py checkpassed.tools/assurance/check.pypassed both with and without--base acc357b8.Commit 5: the new checker passes on Linux and Windows. It rejects a 1e-10 change to a price, an edited source-read hash, a changed tolerance, a dropped case and a float turned into an integer.
check.pywith and without--base, the assurance tests andtools/library/check.pypassed. The other 154 Python steps after step 600 that need no build pass on Linux. The 51 Python steps that need a built binary and the 134 cargo steps after it were first run in CI.Commit 4: in a fresh clone with no materialized fixtures, the full Offline Quantitative Comparison job passed: 392 retained QuantLib rows, 973 swap values, 35,820 CDS values and the credit calibration.
check.pyand the assurance tests passed again with the new workflow.CI on
b111510e(run 39447079562): every job passed, including all 946 steps of Bounded Rust Assurance, Excel Add-in (Windows) withdotnet test, Test, Offline Quantitative Comparison, and the Java and QuantLib workflows.Windows:
library_workflowparity (110 cases) and the five parity steps after it passed.RawJsonChunks…test passes.Not verified here
dotnet test) was replayed locally only through its first six parity steps. CI is its first full run, including the 14 P&L P/Invoke tests fixed in commit 4.--basechecks still block wider tolerances and removed coverage.🤖 Generated with Claude Code