From 934ab0090c6982a4e972c0991f656d79821624ef Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Thu, 24 Sep 2026 10:29:49 +0300 Subject: [PATCH 1/3] factory: claim #52 by runner-53651-1790234989141 From 5c7cef04f78d1cba7bb5b67e2baedd57c6611b08 Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Thu, 24 Sep 2026 10:35:36 +0300 Subject: [PATCH 2/3] factory: build #52 --- src/money/cents.ts | 19 +++++++++---------- tests/money.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/money/cents.ts b/src/money/cents.ts index f6321d8..96067d5 100644 --- a/src/money/cents.ts +++ b/src/money/cents.ts @@ -29,22 +29,21 @@ export function parseCents(input: string): number { /** * Split an integer amount of cents evenly across member ids. * - * SEEDED DEFECT (issue #2): this floors every share and drops the - * remainder instead of handing the leftover cents to the first N members, - * so the shares can sum to less than totalCents. `bun test` does not catch - * this because the baseline tests only split amounts that divide evenly. - * The correct remainder rule is documented in - * .claude/skills/handling-money/references/rules.md. + * Remainder rule (.claude/skills/handling-money/references/rules.md): every + * member gets floor(totalCents / n), and the first `remainder` members, in + * the order passed in, get one extra cent, so the shares always sum to + * exactly totalCents. */ export function splitCents(totalCents: number, memberIds: string[]): Record { if (memberIds.length === 0) { throw new Error("cannot split among zero members"); } - const share = Math.floor(totalCents / memberIds.length); + const base = Math.floor(totalCents / memberIds.length); + const remainder = totalCents - base * memberIds.length; const shares: Record = {}; - for (const id of memberIds) { - shares[id] = share; - } + memberIds.forEach((id, i) => { + shares[id] = i < remainder ? base + 1 : base; + }); return shares; } diff --git a/tests/money.test.ts b/tests/money.test.ts index ed1ee3f..f3e6085 100644 --- a/tests/money.test.ts +++ b/tests/money.test.ts @@ -36,4 +36,29 @@ describe("splitCents", () => { const shares = splitCents(2000, ["a", "b"]); expect(sumCents(Object.values(shares))).toBe(2000); }); + + test("gives the remainder cents to the first members", () => { + expect(splitCents(1000, ["a", "b", "c"])).toEqual({ a: 334, b: 333, c: 333 }); + }); + + test("shares always sum to the total", () => { + const cases: Array<[number, number]> = [ + [1000, 3], + [101, 5], + [1, 3], + [2, 3], + [999, 7], + [1003, 4], + ]; + for (const [total, n] of cases) { + const ids = Array.from({ length: n }, (_, i) => `m${i}`); + const shares = splitCents(total, ids); + expect(sumCents(Object.values(shares))).toBe(total); + const base = Math.floor(total / n); + const remainder = total - base * n; + ids.forEach((id, i) => { + expect(shares[id]).toBe(i < remainder ? base + 1 : base); + }); + } + }); }); From 93f3f44a28630ebd280d2756febc2af5e711a5ab Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Thu, 24 Sep 2026 10:39:46 +0300 Subject: [PATCH 3/3] factory: build #52 --- src/money/cents.ts | 6 ++++-- tests/money.test.ts | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/money/cents.ts b/src/money/cents.ts index 96067d5..a5f5cde 100644 --- a/src/money/cents.ts +++ b/src/money/cents.ts @@ -32,14 +32,16 @@ export function parseCents(input: string): number { * Remainder rule (.claude/skills/handling-money/references/rules.md): every * member gets floor(totalCents / n), and the first `remainder` members, in * the order passed in, get one extra cent, so the shares always sum to - * exactly totalCents. + * exactly totalCents. Negative or non-integer totals are out of scope for + * the rule and keep the plain floored share, unchanged from before. */ export function splitCents(totalCents: number, memberIds: string[]): Record { if (memberIds.length === 0) { throw new Error("cannot split among zero members"); } const base = Math.floor(totalCents / memberIds.length); - const remainder = totalCents - base * memberIds.length; + const remainder = + Number.isInteger(totalCents) && totalCents >= 0 ? totalCents - base * memberIds.length : 0; const shares: Record = {}; memberIds.forEach((id, i) => { shares[id] = i < remainder ? base + 1 : base; diff --git a/tests/money.test.ts b/tests/money.test.ts index f3e6085..b4351a9 100644 --- a/tests/money.test.ts +++ b/tests/money.test.ts @@ -61,4 +61,9 @@ describe("splitCents", () => { }); } }); + + test("leaves negative and non-integer totals floored as before", () => { + expect(splitCents(-1000, ["a", "b", "c"])).toEqual({ a: -334, b: -334, c: -334 }); + expect(splitCents(10.5, ["a", "b"])).toEqual({ a: 5, b: 5 }); + }); });