# Performance Learned

This note records shared Test environment lessons that should change how we debug repeated
failures. It is intentionally short and operational.

## 2026-09-05 Test2 outbound retry pressure after CampaignMember fix

- Run/commits/environment: Full Inspection `33952376737` on L2
  `e7a56090a2d5172e13b271c03c6ace910559844d`, Core
  `612bdc3b35664a68e9fac98702b729f06214aeed`, AppRelease `0.71.0`, standalone
  Test2; immediate L0 observer `33953623199` and L1 observer `33953622014`.
  The same 285-case inventory and module concurrency 4 completed in 1,465,169
  ms with 266 passed, 2 failed, and 17 skipped. Both failed cases passed in the
  isolated follow-up `33953790109`.
- Profile result: the CampaignMember Email digest was absent and the two UI
  consumers improved by about 19.3 seconds together. The remaining shared
  resource pressure predates that fix: the prior run accumulated 395.996
  seconds of TiDB resource-queue time and this run accumulated 440.038 seconds.
  The largest read-RU owner was the Integration outbound `ClaimNext` query. It
  ran 1,669 times, consumed about 179,619 read RU (36.1% of captured read RU),
  and scanned 1,635 keys per execution on average. During the same window,
  1,279 command turns were persisted back to retry while only 83 new commands
  were inserted. The earlier run already had 937 retry turns and 141,838 RU on
  the same claim digest, so this is a pre-existing retry-pressure class rather
  than a regression caused by direct-id CampaignMember navigation.
- What failed/learned: the approval request hit its 45-second client timeout
  while TiDB resource admission was queued, and the asynchronous record-share
  event started about 185 seconds after enqueue, after test cleanup removed its
  parent. Extending either timeout would hide the shared queue. The Integration
  worker asks for one due outbound command at a time through a correlated
  predecessor check, sort, and pessimistic lock; its scan grew from about 1,226
  keys near the start of the earlier run to 1,933 near the peak of this run.
  Longer inspection wall time also keeps the poller alive longer, reinforcing
  RU pressure.
- Evidence gap and next gate: the standalone L0 observer does not capture the
  retained Integration Service log or summarize the recent durable outbound
  queue by operation, state, and privacy-safe failure code. Add that bounded
  read-only evidence, capture the current queue/index shape and claim plan, then
  fix the retry owner and candidate-selection access path. Do not raise RU,
  admission limits, or test timeouts before the retry class is identified.

## 2026-09-05 Test2 full-inspection RU queue root cause

- Run/commits/environment: Full Inspection `33924880457` attempt 2 on L2
  `725a3f2229515a5bb59db4c4cdae197d54a08831`, Core
  `612bdc3b35664a68e9fac98702b729f06214aeed`, AppRelease `0.71.0`, standalone
  Test2; immediate L0 observer `33950123824` and L1 observer `33950181934`.
  The inspection ran 285 cases at module concurrency 4 in 1,291,555 ms and
  finished with 267 passed, 1 failed, and 17 skipped.
- Profile result: two identical `CampaignMember` lookups by the virtual
  cross-object formula field `Email` scanned about 1.9 million keys each. Their
  TiDB digest consumed about 41,251 RU in total, averaged 9.19 s execution and
  26.67 s resource-queue time, and coincided with the largest queue interval.
  The calls came from the two browser cases that depend on
  `campaign-member-lifecycle-api`; PR `#5664` made their fixture run-owned but
  rediscovered it with `WHERE Email = ...` because `dependsOn` passes ordering
  but no outputs.
- What failed/learned: indexing `CampaignMember.Email` is not a clean option
  because it is derived from `Lead.Email` or `Contact.Email`; switching to
  another non-key lookup would retain the same design error. The API setup
  already receives the exact created `CampaignMember` id. The inspection
  runner should explicitly export that scalar to declared sibling dependents,
  which can navigate by primary key without a second database query. Outputs
  must remain module-local, dependency-scoped, explicit, and fail closed when
  absent.
- Separate failure class: the sole browser failure saw Session-service 503s at
  about 06:11:24, before the large CampaignMember scans began at 06:13:58, so
  that failure is not caused by this digest. Similar 503s also appeared in the
  green first attempt. Current L1 session error handling loses Runtime failure
  category and admission-limit fields, so collect those fields before changing
  concurrency, retry budgets, or Runtime capacity.
- Next hypothesis/gate: add the generic explicit case-output contract, change
  both CampaignMember consumers to direct-id navigation, and rerun the focused
  three-case module with immediate TiDB observation. The digest must execute
  zero times and the cases must preserve their browser assertions. Diagnose the
  independent Session 503 with privacy-safe category logging; do not infer an
  admission fix from HTTP status alone.

## 2026-09-05 Test2 post-RLS and Email lookup full inspection

- Run/commits/environment: Full Inspection `33924880457`, L2
  `725a3f2229515a5bb59db4c4cdae197d54a08831`, Core
  `612bdc3b35664a68e9fac98702b729f06214aeed`, AppRelease `0.71.0`, standalone
  Test2. The workload ran 285 cases across six serial role lanes and completed
  in 1,264,050 ms with 268 passed, 0 failed, and 17 skipped.
- Profile result: `sales/opportunity` was the largest module at 450,239 ms.
  The slowest case, Quote approval discount routing, spent 110,096 of 110,119
  ms in 45 API calls; approval submissions were about 9.3 s each and
  QuoteLineItem writes were about 5.2 s each. Quote average-discount coverage
  spent 55,412 of 55,420 ms in 26 API calls, led by five QuoteLineItem writes
  at 4.3-5.4 s. The duplicated Lead campaign-conversion case spent about 30 s
  of each 46 s run in explicit test delays; convertLead itself took 6.8-7.0 s.
  Bulk ingest spent about 21 s moving from UploadComplete to JobComplete while
  its 18 status reads consumed only 4,952 ms in total.
- What changed/failed/learned: the Email guard case improved from 22,334 to
  5,999 ms and its lookup completed on the first attempt. The Task activity
  target query improved from 3,205 to 2,630 ms, while unrelated group writes
  made the containing case noisier. Total suite wall time stayed flat because
  the optimized lookups are a small part of the critical path. Case elapsed
  time alone therefore cannot distinguish application execution, asynchronous
  convergence, and fixed test delay.
- Evidence gap and next hypothesis: the standalone Test2 cutover intentionally
  retired the former monolith log collector, while the surviving D3 collector
  is Test3-only and cannot establish Test2 facts. Add one bounded, read-only
  read-only Test2 observation operations in the existing L1 and L0 owners. They
  capture the exact L1/L0/Worker log window and read TiDB statement-summary
  latency, RU, queue, and scan counters without creating another process owner.
  Use it to classify approval, QuoteLineItem, Opportunity reparent, convertLead,
  and Bulk worker latency before changing timeouts or test waits.

## 2026-06-29 Test2 Connector Inbound Drain

### What did

- Reran formal test2 main/main/main deploy after L0 and L2 fixes landed.
- Hit a deploy failure before Env Inspection:
  `_System_ConnectorEvent` still had non-terminal connector inbound rows before
  Catalog candidate convergence.
- Compared this run with earlier failures in issue #3477 instead of patching the
  immediate SQL state.

### What failed

- The previous fix moved the queue drain before Catalog candidate convergence, but
  the test reset stage stopped the existing backend before resetting the TiDB
  branch.
- After reset, the deploy waited for connector inbound rows to drain while no
  worker was running.
- The default drain timeout was also shorter than the L0 connector processing
  stale-lease window.

### What we learned

- Repeated failures in the same area are a design smell. Stop and draw the state
  machine before adding another local patch.
- Do not bypass the pre-convergence connector ownership guard. It protects a
  real invariant: old connector inbound rows must not be owned by a pack install
  that is being re-bound.
- Do not mutate connector event rows from L2 deploy scripts just to unblock a
  test. Let the L0 worker process or reclaim them according to the connector
  protocol.
- A queue drain is only meaningful when the owner worker can run. Waiting after
  the worker is stopped is not validation.
- Timeout budgets must match the mechanism being observed. If L0 can reclaim a
  stale processing lease after about 300 seconds, a deploy drain timeout below
  that can produce false blockers.

### What next

- For test branch reset, briefly restart the previous runtime worker after the
  reset, wait for connector inbound drain, then stop it before Catalog candidate
  convergence.
- Keep fail-closed diagnostics. If the queue still does not drain, report pack,
  category, status, age, and claimant evidence instead of guessing.
- When case counts move from 1 failure to many failures, compare runs by deploy
  event and backend build under test before deciding it is a new product bug.

### Follow-up from run 28387716179

- The first reset-drain implementation used the current deploy artifact
  directory to find the previous runtime summary. A fresh deploy has no
  `deploy-runtime.env` there yet, so the branch-reset drain skipped and the
  apply-stage drain waited with no worker running.
- The correct source of truth is the slot's deploy history under
  `ENV_ROOT/artifacts/deploys/*/deploy-runtime.env`, excluding the current
  deploy id and skipping entries whose runtime build has been cleaned up.
- Diagnostics changed the next action: instead of guessing "the worker did not
  work", the log proved "the worker never started because the old summary lookup
  was wrong".

### Follow-up from run 28388511804

- The previous runtime summary lookup worked and the deploy attempted to start
  the previous backend after branch reset, but health never passed.
- The failure log did not include the Go process fatal line because the existing
  systemd unit still wrote stdout/stderr to the previous deploy's
  `BACKEND_LOG_DIR`, while the health diagnostics tailed the current deploy log
  directory.
- Correct handling: carry `BACKEND_LOG_DIR` from the selected previous
  `deploy-runtime.env` into the branch-reset drain health check. If the old
  runtime still crash-loops, the next run must show the real fatal cause before
  we change queue or worker behavior.

### Follow-up from run 28389250443

- The deploy still failed in branch-reset drain, and the selected previous
  summary did not surface a usable backend log path.
- The reliable log source for a running/stale backend service is the installed
  systemd unit itself: `StandardOutput=append:...` and
  `StandardError=append:...`.
- Correct handling: startup diagnostics must print `systemctl cat` and tail the
  log files declared by the unit. Do this before changing queue semantics or
  replacing the worker path, because otherwise the fatal startup cause remains
  hidden.

### Follow-up from run 28389830950

- The improved diagnostics exposed the real fatal: after TiDB branch reset, the
  restored `_System_InstalledPack.SourceURI` for `pingcap-hubspot-companion`
  pointed at a stale absolute path from another runner/release root.
- L0 readiness was correct to fail. A source-backed active pack whose source
  root cannot be resolved is not safe to serve.
- The L2 reset-drain step should not blindly start the previous backend. First
  probe whether previous runtime packs actually have non-terminal connector
  events. If the queue is empty, skip the previous backend entirely.
- If a drain is required, rebind only active local previous-runtime pack source
  paths to the selected previous deploy's accessible build root before starting
  the previous backend. Do not normalize status, mutate hashes, skip readiness,
  or directly edit connector events.

### Follow-up from run 28390628219

- The source rebind fix worked: after reset, the deploy detected real connector
  backlog and rebound both active local runtime pack source roots to an
  accessible previous build root before starting the previous backend.
- The previous backend did not fail with `source.not_found`; it was still doing
  startup readiness verification when the default two-minute health window
  expired. The last visible line was a pack verify step, not a fatal error.
- Correct handling: branch-reset drain is a cold-start path and should use the
  same class of backend health budget as Formal Staging cold starts. Keep the longer
  wait scoped to the temporary previous-backend drain worker.
- Also enlarge failure diagnostics tails. The process had restarted several
  times before the final visible verify, and a 20KB tail can hide the first
  fatal line. Missing evidence is itself a signal to improve diagnostics before
  guessing at product behavior.

### Follow-up from run 28391332599

- The longer health budget was useful as diagnostics, but it was not the root
  fix. It exposed the real fatal: the selected previous build root had a
  different `pingcap-hubspot-companion` payload hash than the one restored in
  `_System_InstalledPack`.
- "Previous runtime" is not the newest deploy artifact by mtime after a TiDB
  branch reset. The source of truth is the DB-restored installed-pack payload
  hash.
- Correct handling: scan previous deploy summaries and only select one whose
  active local runtime pack tree hashes match the restored active
  `AppliedPayloadHash` values. Skip mismatched candidates instead of starting a
  backend that L0 readiness must reject.

### Follow-up from run 28392025256

- The hash-matched previous-runtime selection did its job: every local previous
  deploy summary was rejected because its pack tree hash did not match the
  reset DB's restored `AppliedPayloadHash`.
- The remaining failure is no longer an L0 readiness or L1 payload problem. The
  test2 branch snapshot contains stale connector inbound rows, but the runner no
  longer has the exact runtime artifact generation needed to safely start the
  old worker and drain them.
- Correct handling: fail fast after reset when no hash-matched previous runtime
  artifact is available and connector backlog still exists. Classify it as a
  test environment/backlog blocker: restore the matching runtime artifact,
  rebase/reset the test branch baseline to a clean snapshot, or use the approved
  cleanup runbook. Do not SQL-delete connector rows from deploy code.

### Follow-up from run 28393342242

- The fail-fast guard worked and shortened the failure to about two minutes, but
  it also exposed a deeper design issue: after a test branch reset, the restored
  connector backlog may be inherited from the branch parent, not from the
  previous test2 runtime.
- In that shape, "start the previous test backend to drain" is the wrong model.
  The restored rows belong to a pack hash generation for which no test runtime
  source exists, and processing parent-branch connector work inside test2 would
  be less correct than quarantining it.
- Correct handling: when reset leaves connector rows but no hash-matched test
  runtime source exists, mark those inherited non-terminal rows `dead` with a
  deploy-time LastError. This preserves operator evidence, keeps L0's live-queue
  invariant intact, and avoids deleting rows or running the wrong backend.
- For latency regressions, use p90 as the watchdog decision statistic rather
  than p50. p50 only describes the typical request and can hide the slow tail
  that causes user-visible flakes; p90 keeps the tail visible while remaining
  cheaper and less brittle than `max`.

### Follow-up from runs 28395189956 and 28396548019

- Full test2 inspection after the deploy fix produced 16 failures, but a focused
  serial rerun of those exact cases produced 14 passes and only 2 failures. That
  is strong evidence that most of the red matrix was caused by inspection load
  and module scheduling, not by 14 independent product regressions.
- The failure pattern matched the load hypothesis: p90 latency modules were
  running in the same per-role concurrency pool as write-heavy functional
  modules, which amplified tail latency and caused edge 503/504s in otherwise
  passing write cases.
- Correct handling: keep p90, but isolate performance-only modules from
  functional/mixed modules. Functional/mixed modules may still use the configured
  module concurrency; performance-only modules run afterward with concurrency 1
  so the measurement does not perturb the workflow it is supposed to observe.
- Do not silently fall back to p50. Unknown sampling statistics should fail the
  runner, because a typo that becomes median latency can hide the exact slow
  tail watchdog is meant to catch.
- The focused serial run still failed Account and Contact record-detail p90.
  Treat those as the remaining true performance investigation, not as evidence
  that the 504/500/search failures are all product bugs.

### Follow-up from runs 28398412890 and 28399441914

- After isolating performance-only modules, the formal test2 inspection improved
  to 206 passed / 3 failed / 13 skipped. Functional coverage was 171 passed / 0
  failed, so the remaining full-run failures were all performance-class cases.
- A focused serial rerun of those 3 failures passed 3/3. That means they were
  not stable product functional failures.
- One failure had a concrete test-design root cause: `lead-lookup-search-api`
  was included under both admin and SDR roles while using only `__runId` as its
  business key. In a full run admin created the run-scoped Lead first; SDR then
  reused that admin-owned record and search returned empty under SDR visibility.
  Role-scoped case data (`__roleId` + `__runId`) prevents cross-role self-seed
  collisions without weakening the product assertion.
- Browser p90 failures must preserve sample timing detail on the failure path,
  not only on successful runs. Without the failed sample timing breakdown, a
  p90 tail cannot be classified as route/navigation, ready-response, or UI-busy
  time with enough evidence.

### Follow-up from runs 28399932760 and 28401772313

- A later full run still reported 206 passed / 3 failed / 13 skipped. The
  failures were not all the same class: one approval submit returned a 504, one
  record-read API case returned a real backend/nginx 404 for the shared
  Opportunity id, and one SDR search p90 exceeded the 3000ms budget.
- A focused serial run of the sales latency module passed 17/17 using the same
  resolved Opportunity id. That proves the record was not permanently missing
  and that the sales latency module sequence does not deterministically delete
  or hide it. It does not yet prove whether the full-run 404 came from record
  access, a read-target inconsistency, or transient full-run pressure.
- Code inspection found a separate L2 runner reporting bug: `perfMetrics` used
  `Number(step.maxLoadMs)` to decide whether a browser step had a latency
  budget. In JavaScript, `Number(null) === 0`, so warm-up browser steps whose
  artifact has `maxLoadMs: null` were incorrectly counted as measured
  performance duration.
- Recomputing the focused artifact with the fixed extraction changed browser
  detail-load report durations from warm-up loads to the actual sampled p90:
  Opportunity 6206ms -> 2993ms, Account 5877ms -> 2851ms, Contact 5717ms ->
  2825ms. The API record-read metric stayed 1201ms, so the fix is scoped to the
  report extraction bug and does not hide API latency.
- Correct handling: when a case passes but the summary perf number looks over
  budget, inspect the runner's artifact extraction code before treating it as a
  product regression. A dashboard/reporting bug is still worth fixing, but it
  must be separated from runtime root cause analysis.
- Remaining investigation should focus on evidence for the full-run-only
  behaviors: exact backend request id logs around the approval 504, and a
  same-request-path probe that compares SOQL fixture selection, REST
  `/sobjects/<object>/<id>`, and UI API record-ui visibility for the record-read
  case under full-run scheduling.

### Follow-up from run 28402807947

- After the warm-up report fix merged, formal test2 inspection improved to 207
  passed / 2 failed / 13 skipped. Functional coverage was 171 passed / 0 failed;
  both remaining failures were performance-only.
- The two failures were real p90 tails, not p50 artifacts:
  `record-detail-load-account-latency` selected 5435ms against a 5000ms browser
  budget, and `parameterized-search-lookup-latency-api` selected 3103ms against
  a 3000ms API budget.
- The report extractor still missed one important evidence path: failed browser
  performance steps live in `detail.failedStep`, not only in `detail.steps`.
  Ignoring `failedStep` made the summary show `0ms / 5000ms` for a real failed
  sampled page load.
- Browser artifacts also need the ready-response request id and trace context.
  Without those fields, a slow page load cannot be tied directly to backend
  permission, record-ui, query, or nginx logs.
- A separate diagnostic lesson: probe SQL must be derived from the current DB
  and current runner code. A stale hard-coded SDR user id and older search SQL
  shape can make diagnostics look authoritative while measuring the wrong path.
  Treat docs, comments, and old issue notes as clues; current code, artifacts,
  request ids, and logs are the evidence.
- Correct handling: do not lower thresholds and do not switch back to p50. First
  repair the evidence path, then compare the exact current SQL variants and
  backend request-id logs before choosing an L0/L1/L2 fix.

### Follow-up from run 28405879108

- Formal test2 inspection on `main` SHA `8fb65fcd` produced 206 passed / 3
  failed / 13 skipped. Functional coverage stayed green at 171 passed / 0
  failed; the gap is performance-only.
- The failed browser cases moved from Account to Opportunity and Contact:
  Opportunity selected p90 6037ms / 6000ms and Contact selected p90 5481ms /
  5000ms. In both artifacts, `gotoMs` and busy wait were small while
  `readyMs` dominated, so the current evidence points at backend record-ui
  readiness rather than frontend navigation/render work.
- `parameterized-search-lookup-latency-api` selected p90 3384ms / 3000ms, with
  samples mostly around 2.2s-2.7s plus 3.3s/3.6s tail samples. This remains the
  Lead substring search tail under SDR RLS, not a functional failure.
- Focused runs are useful but not decisive: focused Account and SDR-search
  reruns passed shortly before this full run. A focused pass only proves the
  case can pass in isolation; it does not prove the full-run p90 tail is gone.
- Correct handling: treat moving object-specific record-detail failures as a
  shared record-ui path until request-id backend logs prove a narrower cause.
  Add evidence when the artifact cannot join every p90 sample to backend logs;
  do not patch case payloads or thresholds to chase the moving symptom.

### Follow-up from diagnostic run 28407290178

- Treat docs, comments, and prior issue notes as clues only. The active facts
  for this phase are the current code tree, the exact watchdog artifact, request
  ids, backend/nginx logs, and live DB diagnostics.
- Current code evidence showed `ContractPolicy.Access` was not honoring the
  `AccessRequest` shape once either fields or record types were requested: it
  called full `EffectiveAccess` and then trimmed the response. That made
  field-only callers pay record-type cost and record-type-only callers pay
  field-permission cost.
- Current log evidence showed request-scoped field-permission bulk loads could
  duplicate inside one request under concurrent first readers. Record-ui samples
  showed repeated effective-access/record-type work; search samples still had a
  single full-user field-permission bulk load plus real SQL/service latency.
- L0 PR `pingcap-inc/NexusCRM#3289` fixes only the generic mechanism waste:
  honor permission projection and coalesce concurrent first field-permission
  bulk loads. It does not claim the Lead substring search SQL tail is fixed.

### 2026-08-21 — issue #5264 Formula Resource cutover candidate

- Run IDs: historical failing CI run `32472040176` / job `96740672599`;
  Test2 Full Inspection `32514955876`; isolated Test2 case run `32516791207`;
  correlated Test2 diagnostic `32551816299`.
- Commits/environment: the Test2 baseline served L2 `992866c36739`, Core
  `0064e8fc4e3d`, and app-packs `44f83412216b`; the fix was developed in
  isolated Core/controlplane/L2 worktrees and had not yet been deployed for
  these baseline runs.
- Workload shape: create a `Credits_Topup__c`, then submit it for approval. The
  historical CI evidence shows about 1.9s create followed by 31.965s approval,
  but does not contain a correlated server trace proving which worker owned the
  parent lane for the missing ~30 seconds.
- Profile/result: current-code inspection confirmed that Flow Formula Resources
  were eagerly materialized at interview start. The L2 before-save Flow could
  therefore not derive Name from the sequence assigned by a later element and
  compensated with a create-triggered after-save self-update. Test2 confirmed
  that executable path. The correlated diagnostic then captured the failure:
  the approval request waited `28,765ms` for Credits parent lane
  `128fce620ec7` and was canceled, while TiDB recorded one
  `SELECT ... FROM credits_topup__c ... FOR UPDATE` blocked for `28,824.698ms`.
  The RecordEvent worker had started the same parent lane for the created Top-up
  and completed its Name-only update only after the approval cancellation. This
  is the opposing child/parent acquisition cycle exposed by the compensating
  self-update, not slow SQL execution. The candidate evaluates Formula
  Resources on access, stamps the final Name in the before-save Flow, and
  removes the after-save Flow through pack lifecycle reconciliation.
- Failed/learned: a 30-second timeout is a load-dependent lock inversion, not a
  deterministic duration on every create. The first collector attempt was
  invalid because it enumerated a malformed systemd unit token; diagnostic run
  `32551816299` fixed the observation boundary and supplied the correlated
  Runtime and TiDB evidence. Removing a Flow file is also not by itself an
  upgrade: released controlplane `v0.2.78` already owns this convergence through
  its pack-level `standard_flow` sweeper, which retires an untouched pack-owned
  definition and disables its trigger subscriptions atomically.
- Next hypothesis/gate: publish one frozen L0/controlplane/L2 BOM, warm-upgrade a
  copy of Test2 or Staging, assert `Credits_Topup_Stamp_Name` has no executable
  active version, and collect correlated request/record/parent-lane SQL/RU logs
  for the create+approval journey. If the ~30-second symptom remains, reopen the
  causal model instead of extending the self-update hypothesis. Profile the
  separate ~11-second approval baseline before claiming #5264 meets `<5s/<8s`.

### 2026-08-25 — issue #5200 Quote term-formula regression candidate

- Runs/commits/environment: Test2 Full Inspection `32862478741` and focused
  serial follow-up `32867876023`; L2 regression commit
  `aa6e0fb60421a741386352381e877a679ea8bb47`; prototype baselines Core
  `bf91ae4c4eae66121d08f32a2c40eece9c69b566`, L2
  `5fc20ad82df6b3628255e7f79752454375ea3323`, and app-packs
  `a0f487a26c158fcb23a74cd7ea09f64d03935f83`.
- Workload/profile result: the exact quote journey regressed from about 41.2s
  to 118.8s (126.9s focused serial). Quote create moved from about 1.7s to
  5.4s, Quote patch from 1.9s to 9.4s, QuoteLineItem create from 2.9s to
  15.9s, and approval submit reached the 30s gateway timeout. The #5200
  metadata expanded the active Quote formula from 69 to 2,176 bytes and the
  QLI derivation expression from 61 to 452 bytes while retaining multiple
  hand-expanded Gregorian calendar resources. Cross-environment evidence on
  the same formula shape reproduced the latency before Flow Runtime v3 and
  SCIM changes, ruling those out as the owning boundary. A request-correlated
  internal SQL CPU profile is still unavailable because the diagnostic
  workflow failed at its OAuth observation boundary.
- What changed/failed/learned: #5200 is a semantic fix, not date formatting. It
  restores integer terms when `addMonths(start, k) - 1 day == end` and retains
  the 30.4375-day fallback. The source uses Apex-style target-month clamping;
  the existing L1 `ADDMONTHS` bundle instead carries Salesforce Formula's
  month-end anchoring and cannot be substituted safely. The clean candidate
  adds only neutral L0 target-month-clamp and calendar-day-difference
  primitives, then owns the hybrid rule once in an L2 formula bundle shared by
  the Quote active formula and QLI before-save Flow. Prototype evaluation
  matched an independent reference across 1,364,574 date windows. Generated
  Quote SQL shrank from 3,223 to 841 bytes and field-reference occurrences from
  42 to 16; the Flow shrank from seven formula resources to one.
- Next gate: harden the primitives with standalone boundary/parity tests, build
  the final pack from the exact candidate BOM, then deploy that frozen BOM to
  Test2. Re-run the same quote journey and three approval cases with correlated
  timings before Full Inspection. Treat the structural reduction as strong
  causal evidence, not as proof of the runtime improvement until Test2 shows
  the Quote/QLI steps return near their pre-#5200 baseline and the 30s approval
  timeouts disappear.
