Repository navigation
feat(revenue): line_number gets its writer on both line items, plus a one-off back-fill (#1828) - #1960
Merged
Conversation
…both line items Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8U3asekbiC7yWoEPWR4Dg
…gner Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8U3asekbiC7yWoEPWR4Dg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8U3asekbiC7yWoEPWR4Dg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8U3asekbiC7yWoEPWR4Dg
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
os-warren
marked this pull request as ready for review
September 25, 2026 14:01
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 #1828
Ruling: batch #163 item 2, letter 1 (comment 5731202044) —
line_numbergets a writer on both line-item objects;readonly: truestays; existing null rows are back-filled once by this PR.What changed
createLineItemNumbering(new factory insrc/revenue/objects/_line-item-price-fill.ts, the shared helper this pair already uses), registered on both objects asopportunity_line_item_line_number/quote_line_item_line_number:beforeInsert+beforeUpdate,runAs: 'system', priority 110.line_number = (max line_number under the parent) + 1. A batch insert addsctx.dispatch.index(every row'sbeforeInsertruns before any row is stored, so without it a batch would share one number).1..nfixture pin intest/seed-consistency.test.tsis untouched and green).ctx.objectinside the handler, never from a factory parameter.scripts/backfill-line-number.ts— the one-off back-fill, in the shapescripts/backfill-owner-id.tsalready established (REST-only, report-only unless--apply, rerunnable).test/line-item-line-number.test.ts— the runtime pin on a real ObjectQL; added toRUNTIME_TEST_FILESintest/runtime-coverage.test.ts(that gate requires every registered hook be named in a runtime file).minor), which carries the run instruction for existing deployments.Mechanism (PM assumption A1, measured)
The engine keeps a payload key that a before-hook wrote through its readonly strip (
hookWrittenKeysin objectql 17.4.0stripReadonlyFields), and drops the same key when the caller supplied it. The engine's own strip warning says it: "a beforeInsert hook does NOT need this because hook-written keys are not caller-supplied". So the stamp lands on a rep's insert with no elevated write at all;runAs: 'system'elevates only the sibling read that computes the max — the smallest elevation available, below the #1807 system sub-flow, because nothing else has to be elevated. The test file keeps the control that makes this reading mean something: with the hook unbound, a rep's suppliedline_number: 99is dropped and the row stores none.Back-fill site (A4)
A REST script cannot write a readonly column (an admin is not a system caller; the strip bites). So the script never sends a number: it sends
{ line_number: null }to each null row, parent by parent, oldest first (created_at, thenid), and the hook's update branch stamps it. It is the repo's existing one-off shape (backfill-owner-id.ts), not a migration framework, and not a scheduled flow —get_recordhas no sort, so a flow sweep could not keep creation order, and a new flow would also have to join the automation docs table in three locales. Rows nobody back-fills heal on their next edit anyway.Concurrency (A3)
Max + 1 is read-then-write. There is no unique index on
(parent, line_number)and the hook opens no transaction, so two inserts under the same parent at the same instant can get the same number. From the UI the only line-item creator is the related-list Add picker, and objectuiRelatedList.tsxawaits each create in sequence, so one user action cannot collide with itself; a collision needs two people adding lines to the same deal or quote concurrently. No locking was added.Measurements
Live, two dev servers on one SQLite file: base (
aa61e1d5) to create the null rows, then this branch on a copy of that DB.line_number: 99--apply1,2,3,null,nullbecomes1,2,3,4,5; quote parent1..4,null,nullbecomes1..6, in creation order; exit 0HOTCRM_BILLING_ENDPOINT)line_items[].line_number= 1, 2, 3 (seed) and 4, 5 (rep-created) — non-null on every lineThe quote line item's insert path was measured on the real engine only (
test/line-item-line-number.test.tsruns every case on both objects); live, the quote side was measured through the back-fill.Gates (at head 946e409)
pnpm verifychain, each step's exit captured: validate 0 · typecheck 0 · lint 0 · lint:i18n-gate 0 · hygiene 0 · hygiene:tokens 0 · build 0 · test 0 (173 files, 3713 passed, 1 skipped).field-no-consumers: 5 findings before, 4 after;crm_quote_line_item.line_numberis no longer named.src/revenue: business semantics 15,193 → 15,626 (ceiling 16,000, headroom 374); authored total 18,303 → 18,736 (ceiling 19,000, headroom 264). No ceiling moved.test/line-item-line-number.test.tswent 14 failed / 10 passed of 24; restored withgit checkout HEAD --,git diff HEADempty, blob hash equal to HEAD's, rerun 24/24.Acceptance notes
LINE_ITEM_FIELDS(organization_id,owner_id,created_by,updated_by,created_at,updated_at,owning_business_unit_id,crm_opportunity): theget_recordfieldsprojection is not what reaches the receiver. Out of scope here; reported to the seat.Generated by Claude Code