{"record":{"id":"232bcdcd1e92dbb9","repo":"HKUDS/Vibe-Trading","slug":"waterfall-does-not-reconcile-on-label-tiers-giv","errorCode":null,"errorMessage":"waterfall does not reconcile on {label}: tiers give {computed!r}, expected {expected!r}","messagePattern":"waterfall does not reconcile on (.+?): tiers give (.+?), expected (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/fundmath.py","lineNumber":1087,"sourceCode":"\n        Raises:\n            ValueError: If the tier amounts do not sum to ``distributable``, or\n                if the per-side totals do not match the tier splits.\n        \"\"\"\n        checks = (\n            (\"distributable\", sum(tier.amount for tier in self.tiers), self.distributable),\n            (\"lp_total\", sum(tier.lp_amount for tier in self.tiers), self.lp_total),\n            (\"gp_total\", sum(tier.gp_amount for tier in self.tiers), self.gp_total),\n            (\"lp+gp\", self.lp_total + self.gp_total, self.distributable),\n        )\n        for label, computed, expected in checks:\n            if not math.isclose(\n                computed,\n                expected,\n                rel_tol=_ALLOCATION_TOLERANCE,\n                abs_tol=_ALLOCATION_TOLERANCE,\n            ):\n                raise ValueError(\n                    f\"waterfall does not reconcile on {label}: tiers give \"\n                    f\"{computed!r}, expected {expected!r}\"\n                )\n\n    def tier_amount(self, name: str) -> float:\n        \"\"\"Dollars that flowed through a named tier.\n\n        Args:\n            name: One of the ``TIER_*`` constants.\n\n        Returns:\n            The tier's ``amount``, or ``0.0`` if that tier is absent.\n        \"\"\"\n        for tier in self.tiers:\n            if tier.name == name:\n                return tier.amount\n        return 0.0\n","sourceCodeStart":1069,"sourceCodeEnd":1105,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/fundmath.py#L1069-L1105","documentation":"The waterfall-level reconciliation check in __post_init__ verifies that summing tiers for each reconciled label (e.g. contributed capital, distributions, carry) matches the expected totals supplied by the caller. A mismatch means the tier breakdown disagrees with the waterfall inputs.","triggerScenarios":"Building a WaterfallResult where tiers were constructed from stale or differently-rounded inputs than the expected totals passed alongside them, so computed != expected beyond _ALLOCATION_TOLERANCE.","commonSituations":"Mixing tiers computed at full precision with expected totals rounded to cents; reusing tiers from a previous run after inputs changed; partial-distribution logic that fills tiers inconsistently with totals.","solutions":["Recompute expected totals from the same numbers used to build the tiers (single source of truth)","Round both tiers and expected totals to the same decimal precision before constructing the waterfall","Rebuild the entire waterfall from current inputs instead of reusing tier objects"],"exampleFix":"# before\ntiers = split_tiers(cash, r=0.2)          # full precision\nwf = Waterfall(tiers=tiers, contributed=round(paid_in, 2))  # mismatch -> raises\n\n# after\ntiers = [round_tier(t) for t in split_tiers(cash, r=0.2)]\nwf = Waterfall(tiers=tiers, contributed=round(paid_in, 2))","handlingStrategy":"validation","validationCode":"computed = sum(t.lp_amount + t.gp_amount for t in tiers)\nassert math.isclose(computed, expected, rel_tol=1e-9, abs_tol=1e-9)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute expected totals from the same intermediate values as the tiers","Rebuild waterfalls from scratch rather than reusing tier objects"],"tags":["fund-math","waterfall","reconciliation","invariant"],"backgroundTag":"data-invariant-violation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}