Subject: [PATCH 1/3] feat(ledger): treat a zero delta as a no-op

From: Ada Fernsby <[email-masked]>
Date: Mon, 2 Mar 2026 09:14:03 +0000

Apply returned a wrapped error for zero-value postings, which the
reconciliation job counted as a failure.

Signed-off-by: Ada Fernsby <[email-masked]>
---
 internal/ledger/apply.go | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/internal/ledger/apply.go b/internal/ledger/apply.go
index 95f499d..ce501c6 100644
--- a/internal/ledger/apply.go
+++ b/internal/ledger/apply.go
@@ -1,14 +1,21 @@
 package ledger
 
-import "errors"
+import (
+	"errors"
+	"fmt"
+)
 
 // ErrShortBalance is returned when a posting would drive the balance negative.
 var ErrShortBalance = errors.New("ledger: insufficient balance")
 
 // Apply posts one delta against the running balance and returns the new total.
+// A zero delta is a no-op and never reports an error.
 func Apply(balance, delta int64) (int64, error) {
+	if delta == 0 {
+		return balance, nil
+	}
 	if balance+delta < 0 {
-		return balance, ErrShortBalance
+		return balance, fmt.Errorf("%w: balance %d, requested %d", ErrShortBalance, balance, delta)
 	}
 	return balance + delta, nil
 }
-- 
2.43.0

---

From: Ada Fernsby <[email-masked]>
Date: Mon, 2 Mar 2026 09:31:50 +0000

Half-up rounding drifts the balance upward over long runs.

Signed-off-by: Ada Fernsby <[email-masked]>
---
 internal/ledger/rounding.go | 17 +++++++++++++++++
 1 file changed, 17 insertions(+), 0 deletions(-)
 create mode 100644 internal/ledger/rounding.go

diff --git a/internal/ledger/rounding.go b/internal/ledger/rounding.go
new file mode 100644
index 0000000..d6f0c19
--- /dev/null
+++ b/internal/ledger/rounding.go
@@ -0,0 +1,17 @@
+package ledger
+
+// HalfEven rounds a minor-unit amount to the nearest multiple of step using
+// banker's rounding, which keeps long runs of postings from drifting upward.
+func HalfEven(minor, step int64) int64 {
+	if step <= 1 {
+		return minor
+	}
+	q, r := minor/step, minor%step
+	switch {
+	case 2*r > step:
+		q++
+	case 2*r == step && q%2 != 0:
+		q++
+	}
+	return q * step
+}
-- 
2.43.0

---

From: Priya Raman <[email-masked]>
Date: Mon, 2 Mar 2026 10:02:11 +0000

Frees the name for the operator guide.

Signed-off-by: Priya Raman <[email-masked]>
---
 docs/{README.md => guide.md} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename docs/{README.md => guide.md} (100%)

diff --git a/docs/README.md b/docs/guide.md
similarity index 100%
rename from docs/README.md
rename to docs/guide.md
-- 
2.43.0