From 5ffb0ce2699b18db001787514c2e718fb2c5ec06 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Sat, 8 Aug 2026 16:40:01 -0500 Subject: [PATCH] chore: regenerate for the forecast verification halt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additive, from robosystems#1109. ComputeForecastResponse gains `halted_at`: the month where the forecast walk stopped because verification failed, or null if it ran the full horizon. Each month's opening balances are the previous month's closing balances, so computing past a failure yields months derived from a known-wrong one rather than merely unverified ones — the walk now stops and names where. ForecastMonthLite.verification_passed keeps its type and gains an honest description. It is tri-state and the third state is not the first: true = rules ran and passed, false = something failed (which halts the walk), null = no rules ran, so the month is unverified rather than verified. Consumers had been treating null and true alike. DO NOT MERGE BEFORE robosystems#1109. This was regenerated against a local stack carrying that branch, not against origin/main — the server does not return `halted_at` yet and does not halt. Merging first would publish a client documenting behaviour the deployed API doesn't have. Gate: 519 passed, ruff + format + basedpyright clean. --- .../models/compute_forecast_response.py | 24 +++++++++++++++++++ .../models/forecast_month_lite.py | 6 +++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/robosystems_client/models/compute_forecast_response.py b/robosystems_client/models/compute_forecast_response.py index bb2bda2..628d10e 100644 --- a/robosystems_client/models/compute_forecast_response.py +++ b/robosystems_client/models/compute_forecast_response.py @@ -27,6 +27,11 @@ class ComputeForecastResponse: base_period (str): Seed month the walk projected from. months (int): Forward months requested. months_computed (list[ForecastMonthLite] | Unset): + halted_at (None | str | Unset): Month (``YYYY-MM``) where the walk stopped because verification failed, or null + if it ran the full horizon. Each month's opening balances are the previous month's closing balances, so + computing past a failure yields months derived from a known-wrong one rather than merely unverified months. When + set, ``months_computed`` ends at this month and is shorter than ``months``; the failing month's facts are kept + so the failure can be inspected. skipped (list[SkippedForecastLite] | Unset): diagnostics (list[str] | Unset): Articulation notes — a missing cash/earnings anchor, schedule contributions with no base-set landing spot, an absent cash-flow structure. Informational; the walk still computed. @@ -38,6 +43,7 @@ class ComputeForecastResponse: base_period: str months: int months_computed: list[ForecastMonthLite] | Unset = UNSET + halted_at: None | str | Unset = UNSET skipped: list[SkippedForecastLite] | Unset = UNSET diagnostics: list[str] | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -60,6 +66,12 @@ def to_dict(self) -> dict[str, Any]: months_computed_item = months_computed_item_data.to_dict() months_computed.append(months_computed_item) + halted_at: None | str | Unset + if isinstance(self.halted_at, Unset): + halted_at = UNSET + else: + halted_at = self.halted_at + skipped: list[dict[str, Any]] | Unset = UNSET if not isinstance(self.skipped, Unset): skipped = [] @@ -84,6 +96,8 @@ def to_dict(self) -> dict[str, Any]: ) if months_computed is not UNSET: field_dict["months_computed"] = months_computed + if halted_at is not UNSET: + field_dict["halted_at"] = halted_at if skipped is not UNSET: field_dict["skipped"] = skipped if diagnostics is not UNSET: @@ -116,6 +130,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: months_computed.append(months_computed_item) + def _parse_halted_at(data: object) -> None | str | Unset: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(None | str | Unset, data) + + halted_at = _parse_halted_at(d.pop("halted_at", UNSET)) + _skipped = d.pop("skipped", UNSET) skipped: list[SkippedForecastLite] | Unset = UNSET if _skipped is not UNSET: @@ -134,6 +157,7 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: base_period=base_period, months=months, months_computed=months_computed, + halted_at=halted_at, skipped=skipped, diagnostics=diagnostics, ) diff --git a/robosystems_client/models/forecast_month_lite.py b/robosystems_client/models/forecast_month_lite.py index d60c3e5..f1ac387 100644 --- a/robosystems_client/models/forecast_month_lite.py +++ b/robosystems_client/models/forecast_month_lite.py @@ -27,8 +27,10 @@ class ForecastMonthLite: cash_flow_fact_set_id (None | str | Unset): Scenario CF FactSet upserted for the month — indirect-method, derived from BS deltas + NI, reconciled to the balancing ΔCash. computed_count (int | Unset): Number of facts emitted for the month across all sets. Default: 0. - verification_passed (bool | None | Unset): Whether every rule evaluated against the month's scenario sets passed - (None = no rules ran for the month). + verification_passed (bool | None | Unset): Whether every rule evaluated against the month's scenario sets + passed. Three states, and the third is not the first: ``true`` = rules ran and all passed; ``false`` = at least + one failed or errored, which halts the walk (see ``halted_at``); ``null`` = **no rules ran**, so the month is + unverified rather than verified. Treat null as absence of evidence, never as a pass. verification_failures (list[str] | Unset): Failed/errored rule messages for the month (capped). """