Declare the auto-drafted contract's defaults as defaults (#1129) - #1523
Merged
Conversation
`quote_on_accepted` supplies `contract_term_months`, `start_date` and `contract_type` because `crm_contract` requires all three and a quote can express none of them. The maintainer's 2026-08-31 ruling settled what they are: an auto-drafted contract is a starting draft an admin completes, not a faithful transcription of what was sold. So the values stay and their provenance becomes visible — a `DRAFT_CONTRACT_DEFAULTS` block that says they are placeholders rather than decisions, with `end_date` documented as derived from two of them rather than as a fourth guess. The block is handler-local, not module scope: an L2 hook body ships body-only and has no module scope at runtime, so a hoisted block would `ReferenceError` in production while passing every plain-function test. The new test's QuickJS leg is what holds that down. The ruling's other half is recorded at the same site: the quote's `shipping_terms`, `billing_address`, `shipping_address` and `description` are deliberately not copied. No behaviour change — the contract document is byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hUuCQStzXGMFSX4dzww5t
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Sep 3, 2026
os-sales
marked this pull request as ready for review
September 3, 2026 07:50
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 #1129
Implements option B of the maintainer's 2026-08-31 ruling: an auto-drafted contract is a starting draft an admin completes, not a faithful transcription of the quote. So none of the values change — what changes is that nothing marked them as placeholders, which is the same complaint #873 recorded one field over, and that one cost a real drift.
The drafted contract document is byte-identical. No behaviour change, pinned by a new test rather than argued.
What landed
1. The three defaults are declared as defaults.
contract_term_months: 12,start_date: todayandcontract_type: 'subscription'are collected into aDRAFT_CONTRACT_DEFAULTSblock carrying the provenance the ruling asked for: placeholder defaults, not business decisions, with the reason each one exists (the contract requires it; the quote cannot express it) and the unfreeze criterion for the alternative.end_dateis documented as derived from two of them rather than as a fourth guess.2. The other half is settled at the same site. A comment records that the quote's
shipping_terms,billing_address,shipping_addressanddescriptionare deliberately not copied — decided, not overlooked — with the structural facts behind that: two of them have no counterpart column oncrm_contractat all,billing_addresshas one and is left for the admin completing the draft, and the contract'sdescriptionis occupied by the draft's provenance sentence.3. A pin test (
test/quote-accepted-draft-defaults.test.ts, 12 assertions) covering the values themselves, the four fields that deliberately reach nothing, and the claim the provenance rests on.Two things measured rather than recalled
contract_typereally has no default of its own. The provenance comment's load-bearing claim is that this hook is the only thing that ever picks a contract type. Measured off the object: six options, no field-leveldefaultValue, and no option carryingdefault: true— the two spellings this repo actually uses (cf.statusandbilling_frequencyon the same object, which do carry one). Both spellings are now pinned, so the comment goes red if that ever changes instead of quietly becoming false.There is no second drafting path. A defaults block covering one of two drafting sites would be a half-fix, so this was checked rather than assumed:
src/objects/quote.hook.tsis the only code path that creates acrm_contractrow. No flowcreate_recordnode targetscrm_contract;contract.hook.tsonly updates andcontact.hook.tsonly counts. The seeded contracts insrc/data/revenue.seed.tsare hand-authored demo rows — which is why they vary by type while every auto-drafted contract is a subscription.Why the block is inside the handler
The ruling names a constant block; this file's own header names the constraint that decides where it can live. An L2 hook body ships body-only and has no module scope at runtime, so a hoisted block is not a style preference — it breaks the body-only path.
That is the one failure this refactor could actually introduce, so it is the one the test measures directly. Ablation, on the committed implementation: hoisting
DRAFT_CONTRACT_DEFAULTSto module scope leaves 11 of 12 assertions green and turns exactly one red — the QuickJS leg:That asymmetry is the point: every ordinary closure-keeping test is blind to a hoisted block. The tree was restored from
HEADafterwards and the file's blob hash re-checked againstHEAD:src/objects/quote.hook.ts(22c684b, identical).Verification
pnpm verifygreen end to end atdb06ad9— validate, typecheck, lint, i18n gate, hygiene, token ratchet, build, and 157 test files / 3297 tests passed, 1 skipped. The token ratchet moved 83,079 to 83,156 in business semantics (ceiling ~85,000, headroom ~1,844); comments are stripped before that count, so the growth is the block itself.Changeset
Empty frontmatter — the sanctioned "this PR releases nothing" declaration that
.github/workflows/changeset-check.ymldocuments, on par with theskip-changesetlabel. Chosen over the label because this is not a CI or housekeeping chore: it is a source change in business logic, and the in-repo record of why those values are what they are belongs with it. Nothing reaches users, so apatchbump would put a line in the release notes about a document that did not change.Scope
Only the ruling. #1253 (a drafted contract's back-link to the quote that produced it) is graded independently and is not folded in here. #714 (boolean
falseinto a lookup) and #873 (thepayment_termscopy) stay out of scope; neither is touched. Option A — the quote carrying a real term and type — is recorded in the code comment as recorded-not-undertaken, with its unfreeze criterion, and is not attempted.🤖 Generated with Claude Code
https://claude.ai/code/session_019hUuCQStzXGMFSX4dzww5t
Generated by Claude Code