fix(flows): move quote pricing and forecast accumulators to CEL value envelopes - #1985
Merged
Merged
Conversation
… envelopes
The six flow value expressions `objectstack validate --strict` flagged as
`{…}` template dialect now use `{ dialect: 'cel', source }` envelopes:
`quote_generation`'s `discount_amount` / `total_price` and the four
`forecast_snapshot` bucket accumulators.
The quote divides `round(...)` by the decimal `100.0`, since CEL divides
two ints as ints. A cleared discount and a null opportunity amount are
guarded with `has()` / `isBlank()` so they still read as 0, as the
template did; a bare `double(null)` errors. Tests pin both, plus the
decimals, through the real AutomationEngine.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WXp8E7s1gzSBje9ZwwqVux
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WXp8E7s1gzSBje9ZwwqVux
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
hotlong
marked this pull request as ready for review
October 2, 2026 21:25
This was referenced Oct 2, 2026
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.
Fixes #1984
Description
Moves the six flow value expressions that
objectstack validate --strictflags as{…}template dialect to CEL value envelopes:quote_generation→create_quotefields.discount_amount/fields.total_price, and the fourforecast_snapshotbucket accumulators (add_pipeline/add_best_case/add_commit/add_won, all fromsumBucket()). User-visible change: none.Type of Change
Related Issues
Fixes #1984
Related to #1983 (left these six on purpose)
Changes Made
round(double(oppRecord.amount) * (DISCOUNT / 100.0) * 100.0) / 100.0and the1.0 - …analogue.round()returns an int in CEL, so the divisor is the decimal100.0(/ 100would integer-divide and drop the cents).DISCOUNTis one spliced fragment,(!has(vars.discount) || isBlank(vars.discount) ? 0.0 : double(vars.discount)): the template read a cleared discount (null / "") as 0, and a baredouble(null)errors, which would have failed the quote. Authored withexpression(src, 'cel')(precedent:event_attendee.object.ts) because thePtag JSON-quotes an interpolated string and cannot splice a fragment.TOTAL + (!has(ITEM.amount) || isBlank(ITEM.amount) ? 0.0 : double(ITEM.amount)), again viaexpression()because the source splices the node's variable names. The guard keeps a null/absent amount at 0 instead of failing the owner's sweep;double()coerces a string amount.* 1rationale abovesumBucketand in the file header no longer describes the template path.patch.Testing
test/flow-quote.test.ts(newdescribe— 1,234.56 at 10% → 123.46 / 1,111.10; cleared discountnulland""→ 0 / full price) andtest/forecast-snapshot-amounts.test.ts(realAutomationEngineviaflow-harness: a null amount sums as 0 and the stale row is still overwritten; decimal amounts keep their decimals).Generate Quotefails for most non-zero discounts: the flow writes raw IEEE-754 products into two 2-decimal money fields, and the rejection never reaches the user #1206's 30% / 70% of 180,000 and the string-amount forecast case.pnpm verifyexit 0 at151392a4(174 files, 3728 passed, 1 skipped;hygiene:tokensclean on main's ceilings — src/revenue business semantics ~15,665 / 16,000, src/sales ~54,323 / 55,000).npx objectstack validate --strict: 14 warnings on base39ba05e2→ 8 on this branch; the 6 removed are exactly the six sites, the 8 left are byte-identical to base (hierarchy-security capability,page:carddescription, six position-routed approval nodes).Ablations (each from the committed state, restored with
git checkout HEAD -- PATH, restoration proved by HEAD blob hash equality and emptygit diff HEAD):/ 100.0→/ 100keeps the centsred (1 failed / 5 passed)double(ITEM.amount)sums a null amount as 0red:expected 9999999 to be 30000DISCOUNT→ baredouble(vars.discount)Behaviour parity probe (same harness, template on base vs CEL on branch, identical outputs): 1,234.56@10 → 123.46 / 1,111.1; discount null / "" → 0 / 180,000; discount "30" → 54,000 / 126,000; 99.99@33 → 33 / 66.99; forecast with null + 1,000.25 + 0.1 + 0.2 → 1,000.5500000000001 in both dialects, and a null-only owner → 0.
Checklist
.changeset/flow-values-cel.md, patch)Additional Notes
Acceptance notes
discountguard. The issue named the null-amount hazard for the forecast only; measuring the template baseline showed a cleared quote discount (null / "") also priced as 0%, and an unguardeddouble(discount)errors on it. Guarded so Acceptance §2 (pricing unchanged) holds; pinned by a test.oppRecord.amountis not guarded:crm_opportunity.amountisrequired+notNull, so a null is unreachable, and a missingoppRecordalready failscreate_quoteon the requiredcrm_account(comment on edgese4a/e4b).flow-harness, which materialises declared columns asnull. Measured directly againstExpressionEngine(17.6.0) with thecelScopeshape: the guard returns 0 for null, absent and"".reset_totals(0) adds to a CEL double without an overload error — measured through the real engine by the tests above.test/flow-scheduled.test.tskeeps a fixture comment saying the string-amount case is caught by "the* 1coercion in the accumulator". Left untouched because Acceptance §3 asks for the existing forecast tests to pass untouched; its assertion still holds (CELdouble()coerces). A one-line wording follow-up if wanted. Carrier: none.Generated by Claude Code