fix(rates): treat a bare flat fee as a project fee, not per-hour - #455
Merged
ralyodio merged 1 commit intoAug 30, 2026
Merged
Conversation
A rate spec with a price and no stated period ("$5000", "250 USDC") is a
flat project fee. parseRate's own comment says so and warns that defaulting
it to per-hour "would silently multiply the invoice by every hour tracked" —
but the guard set rate.per = "hour" instead of "project", the exact bug the
comment describes.
Because the default is already "hour", the guard was a no-op: parseRate("$5000")
returned per: "hour", so chargeFor scaled it by tracked time. Over a 10-hour
job a $5,000 project fee billed as $50,000. With the fix it returns
per: "project" -> flat, billed once by the invoice.
Regression test asserts a bare fee (fiat and crypto) parses as a project fee
and that an explicit period is left untouched. Full suite green.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
parseRate()insrc/rates.mjshas a guard whose comment states the intent plainly:The assignment set
rate.per = "hour"— the exact behaviour the comment warns against. Since the default is already"hour", the guard was a no-op, so a bare flat fee like$5000or250 USDCparsed as an hourly rate.chargeFor()then scales an hourly rate by tracked time, so a$5000fee tracked over a 10-hour job bills $50,000:A project fee is meant to be flat (
amount: null, added once by the invoice):The fix
One line: set
rate.per = "project"in that guard, matching the documented intent. A stated period ($100/hour) is untouched, and$100/agent(a unit but no period) still defaults to hourly as before.Test
Added a regression test in
test/rates.test.mjsasserting a bare fee (fiat and crypto) parses as a project fee, bills flat over a 10-hour job, and that an explicit period is left alone. Verify-first: the test fails on the pre-fix line and passes with the fix. Full suite green (2375 pass / 0 fail / 337 skip).