parseManifest validates types, non-negativity, address validity and duplicate addresses, but it never checks the manifest's internal arithmetic:
sum(entries[].amount) against poolAmount (and dustRemainder),
entries[].amount against the frozen formula
floor(poolAmount * issuesClosed / totalIssuesClosed),
totalIssuesClosed against sum(entries[].issuesClosed) when the field is present rather than derived.
So a manifest with a correct merkleRoot but wrong amount values parses cleanly. claim will still work — it recomputes the Merkle root from those same rows, and the root matches because the amounts are part of the leaf pre-image — but the operator sees numbers that do not add up to the pool, and report prints a pool/dust/allocation summary that contradicts itself. A stale totalIssuesClosed is worse: shares are computed by actions from that denominator, so a drifted value is invisible here and fatal downstream.
Fix: add these consistency checks to parseManifest (ManifestParseError, naming the offending field and the expected value), or expose them as an explicit validateManifestArithmetic(manifest) the CLI calls in claim/report before printing. packages/sdk/test/types.test.ts already has the fixtures to drive it; splitstream-actions remains the source of truth for the formula, so the check must match it exactly rather than rounding differently.
parseManifestvalidates types, non-negativity, address validity and duplicate addresses, but it never checks the manifest's internal arithmetic:sum(entries[].amount)againstpoolAmount(anddustRemainder),entries[].amountagainst the frozen formulafloor(poolAmount * issuesClosed / totalIssuesClosed),totalIssuesClosedagainstsum(entries[].issuesClosed)when the field is present rather than derived.So a manifest with a correct
merkleRootbut wrongamountvalues parses cleanly.claimwill still work — it recomputes the Merkle root from those same rows, and the root matches because the amounts are part of the leaf pre-image — but the operator sees numbers that do not add up to the pool, andreportprints a pool/dust/allocation summary that contradicts itself. A staletotalIssuesClosedis worse: shares are computed by actions from that denominator, so a drifted value is invisible here and fatal downstream.Fix: add these consistency checks to
parseManifest(ManifestParseError, naming the offending field and the expected value), or expose them as an explicitvalidateManifestArithmetic(manifest)the CLI calls inclaim/reportbefore printing.packages/sdk/test/types.test.tsalready has the fixtures to drive it;splitstream-actionsremains the source of truth for the formula, so the check must match it exactly rather than rounding differently.